 Dann würde ich sagen, wir fangen an. Herzlich willkommen zum Talk Go-Für-Programmierer und vielen Dank an Michael, der uns jetzt hier durch den Talk führen wird. Es wird eine Q&A geben, so am Schluss. Da habt ihr dann die Möglichkeit, eure Fragen zu stellen und natürlich im Nachgang sicherlich nochmal im Speaker. Dann würde ich sagen, viel Spaß! Ja, vielen Dank für die Einleitung. Wie gesagt, mein Name ist Michael Stapelberg. Ich befasse mich seit annähernd zehn Jahren mit der Programmiersprache Go. Am Anfang eher weniger und dann 2012 bin ich eher mehr eingestiegen. Dieser Vortrag ist betitelt Go für Programmierer und das mag am Anfang vielleicht ein bisschen seltsam klingen. Ich meine, für wen denn sonst, oder? Aber die Idee ist eben, dass ich mir hier ein bisschen ein Luxus rausnehme und es euch ein bisschen angenehmer mache. Der Luxus, den ich mir rausnehme, ist, dass ich nur ganz kurz über die generelle Einführung in wie ist eigentlich die Sprachsündung und was kann eigentlich die Sprache gehen. Und was ich euch mitgeben möchte, ist quasi die Experience. Wie ist es tatsächlich mit Go zu arbeiten? Ich werde zwei ausführliche Live-Demos in diesem Vortrag haben. Und die Grundlagen, die ich erklären werde, dienen ein Sekunderlein dazu, dass ihr die Demos verstehen könnt, halbwegs. Wenn ihr Fragen habt, die ihr denkt, dass sie super wichtig sind zum Verständen ist, dann könnt ihr euch gerne melden und dann nehme ich euch direkt dran. Ansonsten, wie gerade gesagt, am Ende, wenn wir eine Q&A Session haben. Schauen wir uns erst mal kurz an, was uns in diesem Vortrag erwartet. Wir beginnen mit einem Schnelldruckblick, weil ich davon ausgehe, dass jeder sich vielleicht schon mal so ein bisschen die Sprache angeschaut hat. Oder wenn nicht, dann ist das auch ein Schnell-Einstieg. Das dauert maximal fünf Minuten und dann gehen wir schon direkt in die erste Live-Demo. In der ersten Live-Demo werden wir uns damit befassen, wie installiert man eigentlich Go auf einem Computer, auf dem bis gleich noch kein Go installiert ist. Ich habe hier einen neuen Benutzer auf meinem Computer angelegt mit einem Standard Ubuntu Version 16, also schon ein bisschen älter. Und das heißt, die Schritte, die ich euch zeigen werde, werden bei euch auf jeden Fall funktionieren. Dann werden wir ein bisschen was programmieren und ein bisschen Tests dafür schreiben. Und dann solltet ihr schon mal den ersten Einblick haben, wie Go sich eigentlich so ein bisschen anfühlt, hoffe ich. Und danach gehen wir in das, was Go einzigartig macht. Die Godot-Teen, die Channels, die man benutzen kann, um zwischen den Godot-Teen zu kommunizieren und schlussendlich die Interfaces. Und dann steigen wir noch mal so richtig ein mit einer Live-Demo, in der es um Concurrency geht und um Profiling. Und ich hoffe, spätestens dann habt ihr einen guten Einblick erlangt in, wie sich die Sprache anfühlen und wie sich die Tools anfühlen. Gut, Schnelldruckblick. Zu der Sündachs. Wir haben in Go wie in jeder anderen Programmiersprache auch Datentypen. Natürlich gibt es einen Bullschendatentyp, der entweder war oder falsch speichern kann. Ein String-Datentyp, der natürlich hervorragende UTF-8-Unterstützung hat, wie man das von einer modernen Programmiersprache erwarten würde. Dann gibt es Integer-Datentypen, sowohl anseient als auch signed. Und wenn man da keine Zahl hinten dran schreibt, dann ist es einfach die native Größe. Das heißt, auf einem 64-Bit-Computer wäre das eben ein Integer mit 64-Bits. Und wenn man dann aber sich an Netzwerkprotokolle und Dateiformate und sowas macht, dann schreibt man üblicherweise die Zahl hinten dran. Zum Beispiel eben 8, 16, 32 oder 64, je nachdem, welche Größe man braucht. Und als Besonderheit ist der Uint8-Typ auch unter dem Namen byte verfügbar, denn das ist ja was er ist. Dann haben wir Flies-Commerzahlen mit 32 und 64-Bit-Genauigkeit. Und zu guter Letzt füllen wir hier die Map ein, einfach in anderen Sprachen als Dicts oder Hashes bekannt. Einfach eine Zuordnung von Key zu Value. Die Sündachs ist hier, der Key in dem Fall ist String und das Value ist eben os.fileinfo. Das wäre also vielleicht ein verzeichnes Inhalt, also Dateinamen und deren File-Infos. Zusätzlich können wir uns Arrays definieren. Das wäre hier in diesem Fall ein Puffer mit der Größe 24-byte. Und es gibt eine zugehörige Slice-Sündachs, mit der wir uns quasi in den Fenster auf dieses Array etablieren können. Was ich hier jetzt mache, ist, ich gehe davon aus, dass wir hier mit einem Datenformat arbeiten, welches einfach ein paar Uint32-Werte hintereinandergespeichert hat als 32-Bit Anside Integer. Und wenn ich den zweiten davon laden möchte, dann würde ich eben hier aus dem Binary Package die Uint32-Funktionen auf dem Little-Indian-Typ aufrufen und gebe ihm den Puffer, und zwar den Ausschnitt mit von byte4 bis byte8, das wäre eben der zweite Uint32. Natürlich kommt man mit den grundlegenden Datentypen nur eine gewisse Strecke weit und ab dann setzt man sich die Datentypen zusammen in sogenannte Struct Types in Go. Das sieht dann so aus, wie hier oben definiert. Ich definiere einen Notebook struct mit dem Modellnamen als String und die Breite und Höhe des Displays als Integer. Ich habe mir hier noch notiert in einem Kommentar, dass das Pixel sind. Und die Definition unten drunter ist dann, wie ich eine variable deklariere, mit einem struct literal initialisiert, und die beschreibt einfach das Notebook, von dem ich hier gerade präsentiere. Natürlich haben wir auch Funktionen, und natürlich kann man die Funktionen auch auf einem Receiver-Typ definieren, dann werden es Methoden. Hier oben ein Beispiel für eine Funktion, die Add-Funktion, die einfach zwei Werte zusammenzählt. Man hat A und B als Integer-Parameter, und es kommt ein Integer hinten raus, und definiert ist die Funktion einfach als return A plus B, so weit so gut. Und dann eine Methode auf dem Notebook-Typ, den wir uns gerade definiert haben. Die Methode heißt PrettyPrint, es wird einfach ausgegeben, der Modellname und dann schön formatiert noch die Bildschirmgröße. Der ganze Code, den ich jetzt kurz angerissen habe, also Funktionen und Datentypen und so weiter, organisiert man in Go in sogenannte Packages. Was hier auf der Folie als Beispiel gebracht wird, ist ein Package namens IoUtil, das gibt es auch wirklich, in der Standard-Library ist aber ein bisschen größer, als hier angerissen. Hier habe ich jetzt nur eine Funktion draus gepickt, nämlich die ReadFile-Funktion. Die ist exportiert, weil der erste Buchstabe von der ReadFile-Funktion großgeschrieben ist. Das ist bei Go die Konvention. Alles was großgeschrieben ist, ist exportiert, das ist in anderen Sprachen als public und private bekannt. In Go ist es eben exportiert oder eben privat. Also nicht exportiert. Eine Sache möchte ich jetzt noch darauf hinweisen, beziehungsweise zwei Sachen. Das erste ist, in der Signatur der Funktion seht ihr, dass sie zwei Rückgabewerte hat. Das ist sehr gebräuchlich in Go, dass der zweite Rückgabewert einfach ein Error-Type ist. Man kann beliebig viele Rückgabewerte haben, aber gebräuchlich ist eben, man hat eine Funktion, die gibt was zurück und gegebenenfalls ein Fehler, falls dabei einen Fehler auftreten kann. Und das zweite ist, dass der ReadFile-Kommentar oben drüber nicht nur Kommentar an sich ist, sondern der wandert dann auch direkt in die Doku. Die Doku könnt ihr hier sehen. Das Programm um die Doku zu Browsen nennt sich Godoc. Es gibt auch eine Webseite namens Godoc.org, bei der ihr die Dokumentation von einem beliebigen Package euch anschauen könnt. Wenn das Package noch nicht auf Godoc ist, wird es automatisch transparent heruntergeladen und dann angezeigt. Und hier ist eben wie das Package, was wir gerade gesehen haben, aussehen würde. Ihr seht die Signatur der Funktion und dann eben einfach den Kommentar oben drüber, den wir hatten. Gut, soweit der Schnelldrückblick, damit ihr nicht euch komplett ins kalte Wasser geworfen fühlt, aber jetzt fangen wir auch schon an mit der Hello World Live-Demo. Ich erlaub mich mal, mich zu setzen und dann wechseln wir mal hier rüber. Ich habe hier einfach mal einen Terminal aufgemacht und auf diesem Computer habe ich noch keinen Go installiert. Das sagt er mir jetzt hier auch. Das heißt, wir fangen mal an, ich mache mir jetzt erstmal hier einen Verzeichnis und dann lade ich hier Go runter, die Binärdistribution. Ich pack das Ganze aus und wenn ich jetzt in Go bin, sehe ich ein paar Ausführbare Dateien. Go, Godoc und Go5, da kommen wir gleich noch drauf, was das alles ist. Ich ändere meine Path-Variable, damit die Go-Installation jetzt auch gefunden wird von meiner Shell. Wenn ich jetzt Go ausführere, dann kriege ich hier auch eine sinnvolle Ausgabe. Ich kann mit GoVersion nochmal verifizieren, dass ich auch tatsächlich die aktuelle Version installiert habe und nicht versehentlich eine Alte aus dem Betriebssystem genommen habe oder so. Und jetzt sind wir eigentlich auch schon startklar, was Go angeht. Gut. Jetzt würde ich einen Editor starten, und zwar mit dem fad go slash source slash hello slash hello.go. Das mag jetzt ein bisschen komisch anmutend. In Go sind alle Softwareprogramme und Packages eben auch, mit denen ihr hantiert im sogenannten Go-Path. Und standardmäßig ist der eben das Go-Verzeichnis in eurem Home Directory. In zukünftigen Versionen wird das nicht mehr notwendig sein, aber im Moment ist es eben noch so. So, jetzt starten wir hier einen Editor. Den Editor habe ich jetzt auch noch überhaupt gar nicht konfiguriert, abgesehen von einer recht minimalen Konfiguration. Also das hier ist meine ganze Konfiguration. Zwei Bildschirmseiten in dieser großen Striftart. Und was ich euch jetzt damit zeigen möchte ist, wenn ich hier jetzt einfach mal dieses Package definiere und die Datei schreibe und eine leere Funktion mache, soweit so gut. Was wir aber noch machen sollten ist, das Go-Mode-Package zu installieren für den Editor. Dieses Package gibt es für alle Editorien. Also gibt es auch für Wim, für Visual Studio Code, für was auch immer ihr für den Editor verwendet. Es gibt immer irgendeine Art von Go-Support. Jetzt mache ich den Editor nochmal neu auf. Und wie wir jetzt sehen können unten anhand der Status-Leiste, sind wir jetzt im Go-Mode und nicht mehr im Fundamental-Mode. Man sieht, der Source Code ist auch farbig geworden. Und wenn ich ihn jetzt nochmal speicher, dann müssen wir noch eine Sache erledigen. Nämlich ein Programm installieren namens Go-Imports. Das Go-Imports-Programm ist sehr ähnlich wie das Go5-Programm, welches Source Code automatisch formatiert. Das Go-Imports-Programm macht noch eine Sache mehr. Es stellt sicher, dass alle Import-Statements, die ihr für das Programm braucht, vorhanden sind. Was bedeutet das Ganze? Gehen wir nochmal in unseren Quelltext. Und jetzt sage ich hier beim Editor, compile und sage go run hello.go. Er führt das Ganze auch aus, aber es gibt noch keine Ausgabe, weil das Programm einfach noch keinen Inhalt enthält. Die Main-Funktion ist noch leer. Ich mache hier 500-Printline rein. Und ihr habt gesehen, als ich gespeichert habe, hat er automatisch Import5-Ergänzt. Das ist super praktisch. Und eine andere Sache, die ich nochmal explizit darauf hinweisen möchte, ist, wenn ich jetzt hier die Formatierung änderte, zum Beispiel, weil ich hier das Tab entferne, vor dem 500-Printline. Und wenn ich jetzt speichere, dann wird das automatisch ergänzt. Und ich mache das auf eine kanonische Art und Weise formatiert. Und das macht das Zusammenarbeiten deutlich einfacher. Gut, jetzt haben wir also unsere erste Datei geschrieben. Das Hello World ist da. Jetzt machen wir doch mal ein bisschen was spannenderes. Wie wäre es, wenn wir Little Indian UN16 vorgeben, dass wir mit einem Netzwerkprotokoll arbeiten, indem wir gerade die Werte 0x12 und 0x34 empfangen haben. Sagen wir, das ist irgendwie ein Temperatursensorwert im Internet of Things-Ding. Und dann führen wir das Ganze hier aus. Wir sehen, das Ergebnis ist 133.0. Und jetzt sagen wir, okay, wir sind jetzt mal soweit fertig. Jetzt nur als super vereinfachtes Beispiel. Angenommen, das wäre das komplette Programm, was wir schreiben möchten. Dann würde man jetzt sagen, ja, soweit so gut. Jetzt haben wir das hier geschrieben. Vielleicht wäre es noch schön, wenn wir das Ganze in eine Funktion refactorn würden. Ich nenne Sie jetzt mal hier Decode. Und die soll natürlich einen Input kriegen. Das wäre dann ein Byte Slice. Und Sie gibt einen UN16 und einen Fehler zurück. Und dann sagen wir hier return das und nill als Fehler. Und hier geben wir jetzt den Byte Slice von gerade eben. Also 1, 2 und 3, 4. Hier sagen wir Input, alles klar. Okay, jetzt habe ich also meine Decode Funktion und gebe das Ergebnis aus. Das mag jetzt okay sein für ein einfaches Programm. Aber jetzt möchte man das natürlich auch testen. Jetzt könnte man auch sagen, das ist so simpel, das braucht man nicht testen. Aber tatsächlich wird es euch relativ leicht fallen, einen Testfall zu identifizieren, der in diesem Programm, so wie es jetzt auf dem Bildschirm steht, nicht korrekt funktioniert. Fangen wir also an. Wir definieren, wir starten eine neue Datei, hello underscore test.go, sagen wir der Package Main. Und diesmal definieren wir eine Funktion namens test decode. Die kriegt einen testing.t-Datentyp übergeben. Und was wir jetzt ändern, wir sagen nicht mehr go run, sondern go test minus v. Und jetzt sehen wir hier in der Ausgabe, er sagt, er hat Testdecode ausgeführt und der Test ist passiert. Und sagen wir hier dies. Und jetzt schlägt der Testfehl. Okay, jetzt wissen wir also, unser Testframework funktioniert. Wie wäre es, wenn wir jetzt die tatsächliche Funktion auch testen? Input ist 0x1, 2, 0x3, 4. Schreiben wir das mal so rein. Okay, jetzt wissen wir, die Funktion kann man aufrufen mit Panic, also das Programm funktioniert an sich. Das ist jetzt aber ja natürlich nicht so spannend. Sondern wir schauen jetzt mal und sagen, wenn es einen Fehler gibt beim Decoding, dann lassen wir den Testfehl schlagen mit t.fatal. Und zusätzlich überprüfen wir den Wert. Wir sagen 1, 3, 3, 3, 0 als UN16. Und dann gehen wir hier eine Fehlermeldung aus. Unexpected. Wir machen das sauber, sagen Decode. Unexpected. Value. Got. Want. Okay. So, jetzt funktioniert der Test immer noch sehr gut. Wir haben also nichts falsch gemacht. Und er hat schon deutlich mehr Wert. Jetzt geben wir wirklich was rein. Und wir verifizieren, dass das was rauskommt. Auch das ist, was wir erwartet haben. Gut, jetzt möchten wir ein paar mehr Testfälle haben. Oder ich habe gerade schon gesagt, es gibt mindestens einen Testfall, wo wir uns doch mal eine Vorschleife und definieren hier einen Struct-Datentyp. So. Und in diesem Datentyp brauchen wir also Input. Das unterscheidet sich von Testfall zu Testfall. Dann brauchen wir einen erwarteten Output. Das ist dieser UN16, der dahinten bei rauskommt. Und ob ein Fehler erwartet wird oder nicht. So, jetzt definieren wir uns den Testfall von gerade eben. Da ist nämlich, ups, Input. Das wäre dieses hier. Want. Wäre. Dies hier. Und want error. Könnten wir jetzt hier explizit auf false setzen, müssen wir aber nicht. Denn alle Variablen in Go werden initialisiert mit dem sogenannten zero value. Also der kanonische null oder leerwert für den Datentyp. Jetzt definieren wir uns mal noch einen Testfall. Nämlich Input, leer des Byteslice. Und hier erwarten wir jetzt einen Fehler. In einem leeren Byteslice kann man natürlich auch nichts davon dekodieren, da ist ja nichts drin. Jetzt starten wir das Ganze doch mal und wir sehen, oh, da ist was schief gegangen. Er sagt, der Test ist gefehlt, denn wir haben hier eine Panic. Und wenn wir uns die Panic jetzt mal anschauen, sehen wir, hier ist der UN16 Aufruf und der UN16 Aufruf geht direkt in den Panic. Gut, gehen wir zurück zu unserem Hello.Go. Und hier sollten wir vielleicht die Länge des Eingabewertes prüfen und dann hier einen Fehler zurückgeben. Okay, so jetzt sagt er hier zwar not enough bytes, aber der Test ist immer noch fehlgeschlagen. Das liegt daran, dass wir hier die Fehler auch ordentlich überprüfen sollten. Es gibt zwei Fälle, wir erwartenden Fehler und der Fehler ist nil. Dann ist was schief gegangen, da sagen wir hier unexpectedly did not encounter an error. Und ansonsten, wenn wir keinen Fehler erwarten, aber einen Fehler auftrat, dann ist auch was schief gegangen. So, jetzt passt das auch alles. Gut, soweit das erste Beispiel, eine Sache möchte ich jetzt noch kurz demonstrieren. Wir haben hier also die Hello.Go und Hello.AnderscoreTest.Go. Ich habe bislang immer Go Run verwendet, einfach um das direkt zu kompilieren und auszuführen. Wir können auch mal Go Build verwenden und schauen, was dabei rauskommt. Wir haben hier eine Datei namens Hello, die ist jetzt so um die 2 Megabyte groß. Genau, genau 2 Megabyte. Die Datei ist eine Ausführbare Datei für 64-Bit AMD64 und statisch gelingt. Eine Sache, die ich recht cool an Go finde, ist, dass wir super einfach cross-compilen können, indem wir einfach die Go-Arch-Umgebungsvariable setzen und jetzt sehen wir, okay, es ist immer noch eine Ausführbare Datei, aber diesmal für ARM64. Das Gleiche funktioniert auch mit allen anderen unterstützenden Architekturen unter Go. Okay, so viel dazu. Das war die erste Live-Demo und jetzt steigen wir doch mal ein, in was Go einzigartig macht. Anfang möchte ich mit den Go-Routinen, die sicherlich viele von euch zumindest schon mal davon gehört haben. Das Beispiel, was wir hier haben, ist relativ simpel. Im oberen Abschnitt geben wir die aktuelle Uhrzeit aus, dann schlafen wir für eine Sekunde die aktuelle Uhrzeit raus. Wenig überraschend seht ihr das auf der rechten Seite in den Kommentaren, also 23.00 Uhr und dann 23.01 Uhr ausgeben wird. Unten drunter haben wir das gleiche Programm so verändert, dass wir vor dem Time.Sleep das Go-Keyword geschrieben haben. Dadurch wird Time.Sleep in einer neuen Go-Routine ausgeführt. Was jetzt hier passiert, ist, dass die Funktion immer noch am selben Zeitpunkt gestartet wird, aber das nicht mehr gewartet wird, bis sie zurückkehrt, sondern sie wird in einem unabhängigen, einen Thread oder eben Go-Routine in Go-Taminologie ausgeführt. Das heißt, die Ausgabe ist jetzt 23.001 und dann nochmal 23.001, weil eben auf das Sleep nicht gewartet wird. Das war es auch schon zu Go-Routine, aber jetzt stellen sich natürlich ein paar Fragen. Zum Beispiel, was wenn ich denn darauf warten möchte, dass meine Go-Routine fertig werden? Das könnte man über folgendes Pattern erledigen. Hier definieren wir uns in der ersten Zeile eine Variable vom Typ Sync.WadeGroup. Und dann gehen wir in einer Schleife durch Files durch, wo es einfach, sagen wir, ein Slice von Strings ist mit Dateinamen. Und für jeden dieser Dateinamen, die wir verarbeiten möchten, zählen wir die WadeGroup um eins hoch. Eine WadeGroup ist nämlich nichts anderes als ein Zähler, aber ein Zähler, der Go-Routine safe ist, also der von mehreren Go-Routinen gleichzeitig verwendet werden kann, ohne dass man zusätzlich noch einen Mutex braucht. Weiter unten haben wir dann Go, wieder das Keyword, um eine neue Go-Routine zu erzeugen. Aber diesmal rufen wir nicht eine bestehende Funktion auf, sondern definieren uns eine Funktion direkt hier im Source und rufen sie dann direkt auf. Das ist ein Function Literal und in diesem Function Literal rufen wir die Workerfunktion auf, die jetzt unsere Datenverarbeitung darstellen soll in diesem Beispiel den Rückgabewert ignorieren wir für den Moment noch und dann sagen wir aber WadeGroup.Dun und das ist wieder das Runterzählen. Und ganz am Ende sagen wir dann WadeGroup.Wade und was jetzt hier passieren würde, wenn wir angenommen 5000 Datein in dem Verzeichnis haben, was wir verarbeiten möchten, dann würden wir also in der Schleife durchgehen, 5000 mal den Zähler hochzählen, 1000 Go-Routinen erstellen und am Ende warten, bis die alle wieder zurückgekehrt sind. Das kennt man in anderen Sprachen, also unter der Join-Funktionalität oder Thread.Join oder wie auch immer. Eine Besonderheit gibt es hier noch, nämlich die zweite Zeile in der Range Loop. Da kopieren wir die Function, sorry, die FileName Variable. Das muss deswegen passieren, weil das Range Keyword die Schleifenvariable immer wieder überstreibt. Was sonst nämlich hier passieren würde, wenn wir ganz viele Go-Routinen starten, aber die dann alle auf die gleiche Datei gehen oder es gibt einfach eine Art-Race-Condition und je nachdem, wann die ausgeführt werden, geht das auf die falsche Datei. Das ist ein bisschen ungeschickt und es ist tatsächlich, man möchte sagen, ein Fehler im Sprachdesign, der hoffentlich irgendwann noch korrigiert werden kann. Das werden wir sehen. Okay, jetzt haben wir jedes Mal Beispiele gehabt, bei denen wir eine Go-Routine starten und der Eingabeparameter war schon klar und der Ausgabeparameter wurde, wurde ignoriert. Das ist natürlich nicht so spannend. Wie kommunizieren wir jetzt Zwischen-Go-Routinen? Dafür gibt es die Channels. Channels sind Go-Routine safe, genauso wie die Weight Group von gerade eben. Das heißt, man braucht keinen Mutex. Für normale Variablen würde man den Mutex brauchen, wenn man die Zwischen-Go-Routine verwenden würde. Was Channels machen ist, sie speichern Werte bzw. sie senden Werte an andere Go-Routine, je nachdem, wie man sie einsetzt und das erkläre ich gleich noch. Channels haben eine FIFO-Semantik, und Channels können dazu benutzt werden, um Go-Routine zu blockieren bzw. zu endblockieren. Schauen wir uns einmal ein Beispiel an. Hier haben wir ein Pattern, das nennt sich Task Queue, und was wir hier wieder machen wollen, ist genau das gleiche wie vorher. Wir möchten wieder Dateien verarbeiten, aber diesmal ist es so, dass wir Milliarden von Dateien haben. Das heißt, wir wollen vielleicht nicht Milliarden von Go-Routine auf einmal haben, weil sagen wir das überlastet den Server und dann mögen das die Leute nicht die den Server betreuen, sondern nette Nutzer und limitieren uns auf 6 gleichzeitig Verarbeitungsstränge. Was wir hier also machen ist, in der zweiten Zeile haben wir eine For-Loop, die von 0 bis 6 zählt und die Funktion Work-Loop in einer neuen Go-Routine startet. Das heißt, ab hier haben wir 6 weitere Go-Routinen, die in der Funktion Work-Loop hängen. Die Funktion Work-Loop ist auf der rechten Seite der Folie definiert. Man sieht, das ist auch wieder einfach eine For-Loop, diesmal über ein Channel. Eine For-Loop über ein Channel liest so lange Werte raus, bis der Channel geschlossen ist. Für jeden Wert, den wir erhalten, treffen wir auch wieder die Worker-Funktion auf. Soweit so gut. Auf der linken Seite haben wir jetzt in der nächsten Zeile eine For-Loop mit einem Rage-Arrange-Statement, welches zuerst den Dateinamen validiert. Also angenommen, das wäre jetzt hier eine Validierung der Datei, die sagen wir 3 Sekunden dauert und die eigentliche Verarbeitung dauert 10 Sekunden. Und was wir jetzt hier machen ist, sofern die Datei validiert wird, wird der Dateiname in den Channel geschrieben. Das macht eben dieser File-Operator hier. Und die ersten 6 Iterationen geht das Ganze einfach durch. Das heißt, der Channel aus der Hauptgoroutine kriegt einen Dateinamen reingeschrieben und eine von den 6 Goroutinen, die wir gerade gestartet haben, liest ihn dann raus und fängt an zu verarbeiten. Nach dem 6. Mal geht das immer noch. Denn wenn wir in die erste Zeile schauen, sehen wir, dass wir einen Channel vom Typ String definiert haben mit einem Puffer von 2. Das heißt, nach dem 6. Mal können wir noch mal reinschreiben und speichern das und danach blockieren die Schreibvorgänge in den Channel. Das heißt, die Range Loop, die Datein validiert, hält dann an. Denn der Ausführungsfluss wird einfach angehalten bei dem Schreibvorgang. Und sobald dann eine von diesem Workloop-Routine fertig ist mit der tatsächlichen Verarbeitung, kann sie wieder einen neuen Wert aus dem Channel lesen und dann geht es auf der linken Seite auch wieder weiter. Das ist eben das Blockieren und Endblockieren von den Goroutinen. Es gibt noch andere Anwendungsfälle zum Beispiel kann man sie als Semaphore verwenden. Hier habe ich ein Beispiel auf der Folie, was einen kleinen Http-Server darstellt. Angenommen, das ist jetzt ein kleines Programm, welches ihr auf eurem Cluster laufen lasst auf jedem Note, um das Tempverzeichnis leer zu räumen, periodisch, oder wann auch immer der Speicherplatz in dem Cluster knapp wird. In der Main Funktion ganz unten seht ihr, es wird ein Http-Händler definiert für den Slash Clear Temp Endpoint. Das ist die Tempfunktion, die weiter oben definiert wird und dann wird einfach der Http-Server startet und in der Tempfunktion möchten wir jetzt sicherstellen, dass die Tempfunktion nur ein einziges Mal ausgeführt wird. Wir haben mehrere Cluster-Scheduler gleichzeitig laufen für Redundance und die würden alle sagen, wir müssen hier Temp aufräumen, dann soll ja nur einer das Temp aufräumen, damit die sich nicht gegenseitig in die Quere kommen und dann der eine Fehlermeldung ausgibt, weil der andere eben Dateien unter der Nase löscht. Wir möchten hier also one-at-a-time Semantik enforceieren und wie machen wir das? Wir benutzen einen Channel, den wir uns ganz oben erstellen. Der Channel hat eine Kapazität in seinem Puffer und hat einen leeren struct datatype. Das heißt, wir möchten damit ausdrücken, dass uns der Wert egal ist, der in diesem Channel steht. Uns geht es nur darum, den Channel und seinen Speicherplatz zu verwenden. Dann in der Tempfunktion schreiben wir einen leeren Wert in den Channel, damit akquirieren wir quasi einen Token aus diesem Semar vor und dann sorgen wir dafür, dass am Ende der Funktion dieser Wert wieder rausgelesen wird, sodass in dem Channel wieder Platz ist. Wir definieren ein Function Literal sowie vorhin auch mit der Go-Routine. Nur diesmal schreiben wir Differ davor. Und das Differ bedeutet für diesen Funktionsaufruf aus, wenn du den Function Scope verlässt. Das heißt, der ganze Code, der dann hinten dran folgt, den ich jetzt hier nicht auf der Folie ausgeführt habe, würde erstmal ablaufen und dann würde das Function Literal ausgeführt, womit der Semar vor wieder released wird. Gut, kommen wir noch zu den Interfaces und dann geht es auch schon an die nächste Live-Demo. Das Interface, was ich hier definiert habe, ist ein Interface, um über Blob-Storages zu abstrahieren. Also angenommen, ihr möchtet einen Cloud-Service schreiben, sagen wir einen Attachmentserver, der, sagen wir, Gmail-Attachments ausliefert oder was auch immer und diese Attachments sind in einem Blob-Storage gespeichert, aber euch als Entwickler von dem Attachmentserver ist eigentlich egal, ob das jetzt der Amazon Blob-Storage-Service ist oder der Google Blob-Storage-Service oder der von OVH oder wem auch immer. Ihr findet euch ein Interface mit dem Namen BlobReader und das Interface enthält eine Methode namens ReadBlob, die kriegt einen Key als Parameter, um den Blob zu identifizieren und dann liefert sie einfach den Inhalt von diesem Blob als Byteslice oder gegebenenfalls ein Fehler, falls irgendwas schiefgegangen ist beim Lesen. Euren Attachmentserver designed ihr dann so, dass der einfach einen BlobReader annimmt und jeder beliebige Typ, der jetzt eine ReadBlob-Methode hat, die dieser Signatur entspricht, der dann in den letzten beiden Zeilen wird. In den letzten beiden Zeilen seht ihr das, da konstruiere ich einen Attachmentserver mit einem BlobReader entweder von AWS oder von GCP, also das könnte in der Test-Suite stehen oder so. Das ist eben sehr praktisch und unterscheidet sich ein bisschen von anderen Programmiersprachen dadurch, dass nicht diejenigen Typen, die ein Interface implementieren, sagen müssen, welche Interfaces sie implementieren, sondern die Kontrolle liegt komplett bei demjenigen, der den Code schreibt, der eben diese Interfaces verwendet. Dann kann man das in der Test-Suite eingehen. Jetzt kommen wir zu der zweiten Live-Demo. Wie angekündigt geht es um Concurrency und um Profiling und was wir jetzt machen, ist, wir möchten die Anzahl an Binärpaketen in Debian zählen, die sich im Moment in Debian befinden. Das ist vielleicht so eine Sache, die man gelegentlich mal machen wollen würde und steigen wir doch direkt hier ein. Wir starten also wieder unseren Editor und speichern wieder eine Datei, sagen Package Main wieder einfach eine Ausgabe, um zu testen, dass auch alles ordentlich aufgesetzt ist. Go Run, alles klar, wir kriegen die Hello-Meldung. Dann sind wir ja startklar. So, was wir jetzt als erstes machen, ist, wir definieren uns mal eine Funktion, weil wir wissen, dass das Ganze ein bisschen länger werden wird und dann sagen wir hier, wenn irgendwas schiefgeht in unserer Accountfunktion, dann möchten wir das Programm terminieren. Das ist natürlich nicht immer der Fall, aber für uns in diesem kleinen Programm ist das jetzt mal gut genug. Jetzt müssen wir natürlich irgendwie mit dem Debian Archiv interagieren. Wir könnten jetzt sagen, okay, dann gehen wir einfach auf unseren Lieblingsmirror und klicken uns mal ein bisschen durch die Packages und so, und da werden wir schon das richtige finden. Aber ich habe zufällig ein bisschen recherchiert im Vorfeld für diesen Vortrag und festgestellt, dass es da schon eine Library gibt in Go. Und die ist von Paul Tech veröffentlicht worden. Ich suche mal ein bisschen draus, weil sich das angepasst hat. Das ist die Library, das Package nennt sich einfach Archive und vorher habe ich schon mal mir die Doku ein bisschen durchgelesen und herausgefunden, dass die Funktionen, an der wir interessiert sind, das ist die Cached Release Funktion, die sich Release Information runterlädt beziehungsweise aus dem Cache holt, wenn die schon mal runtergeladen wurde. Die brauchen wir jetzt hier also, ich kommentiere die mal aus. Und wie wir die aufrufen werden, ist, wir sagen Archive.CachedRelease sagen wir Unstable, Debian Unstable, könnten auch Stable oder was auch immer. Und hier kriegen wir jetzt einen Wert zurück vom Typ Release. Wir kriegen einen Release Downloader. Ich weiß noch nicht genau, wofür wir den kriegen und gegebenenfalls einen Fehler. Mit dem Fehler wissen wir schon mal, wenn wir den kriegen, können wir den gleich einfach weitergeben, weil dann ist das Programm zu Ende. Und jetzt sagen wir hier mal, das Release sieht so aus, mit dem % plus V wird einfach der Datentyp ausführlich dargestellt. Also es gibt %V, das überlässt dann dem Datentyp, das ist wieder ordentlich lesbar da. Jetzt sagt uns der Compiler und Defined Archive, was nicht weiter verwunderlich ist, denn das Go Imports Programm hat uns jetzt hier keinen Import dazu gepackt und das liegt ganz einfach daran, dass wir das Archive-Package noch nicht auf unserem System haben. Das können wir also schnell mal fixen. Wir sagen mal hier, GoGetProgramm jetzt macht, es ist letztlich dieses Package und alle anderen Packages, die wir brauchen, um dieses Package zu benutzen, automatisch runter. Das ist jetzt fertig. Wir können hier jetzt auch mal unter Go Source uns anschauen, was da jetzt alles passiert ist. Wie erwartet, haben wir jetzt hier haufenweise Source Code von polltex-go-slash-debian und so weiter. Und hier ist das Archive-Package und noch ein paar andere Packages, die ihr jetzt gebraucht habt hier. So weit so gut. Jetzt machen wir das hier nochmal. Wenn ich das jetzt erst führe, dann sehen wir jetzt auch, dass wir hier die Release-Information bekommen. Und wenn wir damals ein bisschen durchskrollen, sehen wir, okay, eine Debian Release-Information ist im Wesentlichen so eine Liste von Dateien mit zugehörigen Hashes und den zugehörigen Größen und es gibt eine ganze Menge an Dateien. Viele davon brauchen wir einfach gar nicht. Okay, das ist ja schon mal gut zu wissen. Der erste Schritt wäre jetzt, jetzt picken wir uns doch mal die Datei raus, das heißt, ich habe hier auch schon mal natürlich wieder im Vorfeld herausgesucht, dass es auf dem Release-Info-Datentyp eine Funktion namens Indices gibt, welche diesen Index von Dateien so zurückgibt, dass ich für jeden Dateinamen eine Menge an Dateihashes zurückbekomme. Mit %q gebe ich einen Quote of String aus und mit %d ein Integer. So, geben wir mal das Ganze hier aus und jetzt sehen wir schon, okay, jetzt kriegen wir wieder die Dateinamen von vorher, deutlich lesbarer als vorher und die haben alle mal mindestens einen Hash. Das mit den Hashes funktioniert so, für diejenigen, die das nicht wissen, in Debian sind immer mehrere Hash-Verfahren gleichzeitig unterstützt. Falls eines kryptografisch unsicher wird, hat man immer noch die anderen und so kann man eine langsame Transition zwischen den Hash-Verfahren machen. Das Archive-Package hat den Vorteil, dass es transparent die GPG-Signature auf den Release-Infos verifiziert und so aufgebaut ist, dass ich immer mit diesen File-Hashes arbeiten muss, damit quasi von den verifizierten Release-Infos eine Chain of Trust etabliert wird bis hin zu jeder einzelnen Datei, die ich hier runterladen möchte. Okay, so viel dazu. Jetzt filtern wir uns mal die Dateien. Wir suchen im Übrigen eine Datei eine Sonedatei für die ganzen verschiedenen Architekturen, die in Debian unterstützt werden. Es ist nämlich so, dass nicht nur es genügt, sich eine Architektur jetzt anzuschauen, um die Anzahl an Paketen festzustellen, denn es gibt Pakete, die architekturspezifisch sind und es gibt Pakete, die nicht auf allen Architekturen zur Verfügung stehen. Das heißt, wir müssen uns jetzt wirklich alle Architekturen anschauen und müssen dann deduplizieren, damit wir rausfinden, wie viele Pakete in Debian sind. Wir sagen, if not strings.hasprefix filename und dann sagten wir main.minus dann wird übersprungen und das machen wir jetzt erstmal sehen wir hier okay, das sieht ja schon besser aus. Es sind aber immer noch viele Zeilen. Wieso ist das so? Naja, wir haben hier zum Beispiel packages.xz und dann haben wir weiter oben packages.gz und packages ohne Kompressionsverfahren. Wie bei den Hashverfahren auch gibt es in Debian eine Liste, die nicht unterstützt werden. Wir sind aber nur an einem interessiert, deswegen sagen wir if not strings.hasprefix slash packages.gz und jetzt kriegen wir eine Liste von den aktuellen Architekturen in Debian. Gut, das sieht doch schon mal besser aus. Jetzt nehmen wir uns mal den ersten Hash raus aus der Liste an Hashes. Es gibt immer mindestens einen gültigen und kryptografisch sicheren und das nächste, was wir jetzt machen wollen, ist, wie wir das hier machen können. Es gibt eine Temp-File-Funktion, die nimmt ein ctrl.file-Hash entgegen und gibt uns einen File-Zurück oder gegebenenfalls ein Fehler und die ist auf dem Release-Downloader-Datentyp definiert. Das heißt, da kommt jetzt dieser Release-Downloader ins Spiel, den wir hier vorhin ignoriert haben. Gut, sagen wir also rdl.temp-File und geben ihm hier den Hash. Das nennen wir File und Error, wenn irgendwas schief geht, dann können wir das auf dem Release-Downloader-Datentyp und ansonsten am Ende sagen wir os.remove.f.name. Was Temp-File nämlich macht, ist, es erstellt eine Dateien-slash-Temp. Das ist deswegen nötig, weil man die Dateien natürlich erst herunterladen muss, dann verifizieren muss und dann verarbeiten muss, damit man das kryptografisch sicher macht. Das heißt aber auch insbesondere, dass wir die hinterher aufräumen müssen, wenn wir damit fertig sind. Dafür ist das die First-Datement hier ganz praktisch. Es geht nicht um den Range-Scope, sondern um den Funktions-Scope. Aber in dem Fall ist das okay für uns, das Programm ist klein genug, als dass wir uns das leisten können. Wenn wir das jetzt noch mal ausführen, sehen wir, rdl. haben wir noch gar nicht definiert. Das haben wir hier oben und hier wird das jetzt ausgeführt. Jetzt passiert aber erst mal nichts und jetzt zeigen wir uns doch hier mal dieses Themen-Infos an und sehen jetzt hier, okay, da ist Netzwerk-Datenverkehr daran, dass das sequenziell gerade von dem Mitterer geladen wird, weil wir hier einfach so eine Range-Schleife haben. Das Programm an sich läuft jetzt aber soweit so gut. Jetzt können wir ja erst mal weiter die Funktionalität implementieren, die wir implementieren möchten. Sagen wir also hier mal Filename has Ich habe mir davor schon genau angeschaut, was es für Packages gibt. Da müssen wir jetzt nicht tiefer einsteigen und die erfordert einen Reader. Wenn beim Pausen irgendwas schiefgeht, geben wir einfach den Fehler zurück und dann führen wir das Ganze noch mal aus. Wir sehen wieder auf der rechten Seite Netzwerk-Datenverkehr und hier unten trapfeln jetzt auch schon die Meldungen ein. Hier ist eine gute Unterscheidung, also wir sehen hier IBM S390X Architektur hat 55479 Pakete, und damit ist also klar, wir müssen das wirklich deduplizieren. Machen wir das Ganze doch einfach mal, wir sagen jetzt vor Package gehen wir durch den Index und jetzt bräuchten wir am besten eine Datenstruktur mit der wir quasi Unique Semantik implementieren können. Dafür sagen wir, wir machen uns eine Map von String nach Bool und dann sagen wir ScenePackage.Package gleich true und am Ende zählen wir nicht die einzelnen Architekturen, sondern sagen von dieser Map. Also die Einträge in der Map zählen wir und wenn also der Eintrag mehrfach vorhanden ist, dann wird er einfach überschrieben und sonst wird er eben neu angelegt und deswegen ist das Unique Semantik. Jetzt dauert es wieder in den Moment und dann sollten wir gleich als Ausgabe sehen naah 59482 Packages Okay, wir vermerken uns was mal noch schnell als Kommentar, denn jetzt haben wir uns entschlossen und es dauert uns zu lange wie das Programm hier arbeitet und wir möchten das jetzt schneller machen. Wir definieren uns jetzt also eine Variable vom Typ AirGroup und das AirGroup Package muss ich erstmal noch runterladen. Wir sehen hier das Package liegt in golang.org slash x, das ist der Experimental Namespace. In diesem Namespace leben Packages die noch nicht reif sind für die Aufnahmen in der Standard Library aber die trotzdem schon mal in Ordnung sind. Also das AirGroup Package haben wir uns jetzt hier geholt. Was das macht, ist es das gleiche wie eine WeightGroup die ich vorhin eingeführt hatte aber diesmal kann sie auch mit Fehlerwerten umgehen und wie wir das Programm jetzt parallelisieren ist, wir sagen hier machen wir uns ein Function Literal und geben das an die AirGroup zum Ausführen und hier müssen wir noch einen Nill zurückgeben und hier sagen wir AirGroup.weight, wenn da ein Fehler zurückkam geben wir den an Main zurück und das war es auch schon. Jetzt führen wir das Ganze aus. Lassen wir es hier laufen und wir sehen auch schon auf der rechten Seite ok, 100 MB diese Kunde jetzt haben wir einen ordentlichen Speed aber irgendwas ist schief gegangen. Er sagt hier fatal error concurrent map writes das liegt daran, dass wir hier die Scene Variable benutzen von mehreren Grotto-Teen gleichzeitig ohne dass wir uns einen Mutex darum gebaut haben. Das holen wir doch schon auch schnell nach sagen hier Variable block scene Mutex führen wir ein es ist customary den so zu definieren dass er darüber definiert ist was er schützt und hier unten wo wir Scene verwenden sagen wir SceneMu.lock SceneMu.unlock Lassen wir das Ganze nochmal laufen und jetzt sollte das auch funktionieren ohne Fehler. Alles klar 59482 das ist genau der Wert den wir uns hier notiert hatten das heißt das Programm haben wir jetzt parallelisiert und jetzt dauert es auch schon deutlich kürzer aber irgendwas gefällt mir immer noch nicht wenn wir uns hier nochmal anschauen was hier passiert ist ist wir haben den Netzwerkausschlag gesehen und dann ist er hier vorbei aber die CPU ist die ganze Zeit beschäftigt aber warum eigentlich schauen wir uns das Ganze doch mal an was wir jetzt verwenden werden ist ein Testcase aber ein spezieller Testcase nämlich wir sagen benchmark count und diesmal kriegen wir einen testing.b und wir machen uns eine Schleife von 0 nach 0.n und rufen hier einfach mal Count auf und jetzt sagen wir goTest-v-bench gleich Punkt damit der alle Benchmarkfunktionen ausführt und was jetzt passiert ich starte das hier wieder ist wir sehen wieder ein bisschen Netzwerkdatenverkehr wenn ich das schnell genug gestartet hätte aber hier kriegen wir jetzt ein Report und hier können wir jetzt entnehmen die Benchmarkfunktionalität von unserer Test Suite wurde einmal ausgeführt das liegt einfach daran dass sie furchtbar lange dauert wie das normalerweise funktioniert ist wir haben hier einen Algorithmus drin und der wird für eine Sekunde ausgeführt das heißt so häufig wie in eine Sekunde passt und dann kriegt man hier einen Datenwert wie lange das gedauert hat in dem Fall 6 Sekunden also furchtbar lange und dann kann man sehen quasi und dann kann man auch vergleichen wie schnell der Algorithmus ist und wenn man was daran ändert kann man vorher und nachher messen und so weiter gut was wir jetzt aber verwenden wollen wir haben hier eine zweite Funktion von dem Test-Programm hier mit dem wir uns ein CPU Profile holen das speichern wir nach Temp CPU und dann schauen wir doch mal wo es hier hakt also was hier eigentlich langsam ist okay das hat er gemacht jetzt sagen wir go to pprof Temp CPU und sagen web und was jetzt passiert das ist unser web Browser hat er jetzt in SVG aufgemacht indem wir ein Profil sehen da passiert jetzt einiges aber wir konzentrieren uns mal auf das dass es hier ein bisschen rot hervorgehoben wurde hier sehen wir erstmal okay das Function Literal was wir an die Air Group gegeben haben ist das was den Hauptteil des Programms ausmacht ich meine klar vorher haben wir uns ja nur die Release Infos geholt aber das ist das was sich die einzelnen Packages runterlädt und verarbeitet das ist also soweit erwartet und dann geht hier ein bisschen CPU Zeit in Dateien lesen aber der Hauptteil an CPU Zeit geht in das Control.ParseBinaryIndex und dessen ganze Bestandteile also hier passiert ist furchtbar viel das liegt auch einfach daran man sieht hier von der Runtime, Speichermenagement Funktionen und was wir jetzt hier mal noch machen können ist wir gehen in die Deb Package und was wir hier noch machen können ist wir können jetzt eine Hypothese aufstellen wir haben ja gerade schon gesagt die Funktionen die wir hier übergeben ich pack das mal alles auf den Einbildschirm diese Funktion ist die Teutelfunktion die hat hier noch das TempFile drin aber wir denken das ist wahrscheinlich an dem ParseBinaryIndex liegt das heißt was wir jetzt noch einfach machen können ist wir machen uns eine temporäre Datei sagen us.create TempIndex wenn es einen Fehler gibt direkt zurückgeben ansonsten die Datei schließen und hier sagen wir jetzt I.o.tReader von f nach temp und hier sagen wir Break damit das nur einmal ausgeführt wird und wir eine Datei erhalten jetzt führen wir das Ganze nochmal schnell aus und was wir jetzt erhalten in TempIndex ist einfach eine DebianIndex Datei und wenn wir die hier mal aufmachen dann sehen wir ok hier steht jetzt so Text drin und woran wir eigentlich interessiert sind sind ja gar nicht die ganzen Details die da drin stehen sondern nur wie viele Einträge wir hier haben das heißt eigentlich möchten wir hier quasi die Package-Doppelpunktzellen und das Ganze können wir doch auch gegebenenfalls schneller machen aber jetzt bestätigen wir erstmal wirklich unsere Hypothese ich mache jetzt mal die Änderungen rückgängig von gerade eben damit das Programm wieder so funktioniert wie vorher aber was wir jetzt eben machen ist DebpackageTest.go hier machen wir uns jetzt noch eine neue Benchmark-Funktion Benchmark-Decode und hier wollen wir jetzt das Einlesen von TempIndex sagen wenn es einen Fehler gibt dann wird der Benchmark fehlen ansonsten resetten wir jetzt die Zeit weil wir explizit das Einlesen dieser Datei hier nicht zum Benchmark zählen möchten dann kommen wieder unsere charakteristische Benchmark-Schleife und jetzt machen wir doch hier einfach mal das Paasen und zwar von einem Bytes.newReader und hier benchmarken wir jetzt mal nur die Decode-Funktion lassen uns wieder ein CPU-Profile anfertigen ok jetzt sehen wir hier das sieht ein klein bisschen anders aus als vorhin er hat es jetzt tatsächlich geschafft die zweimal auszuführen weil die ein bisschen schneller ist wenn wir nochmal das GoToolPProf gehen und uns das im Browser aufmachen dann sehen wir jetzt einen deutlich linearer viel hier oben weil jetzt einfach deutlich weniger passiert aber hier unten ist immer noch furchtbar viel Zeug das heißt ja, es ist recht wahrscheinlich dass die Funktion das ist was teuer ist wir könnten jetzt noch wirklich auf Nummer sicher gehen und sagen dass wir mal das Programm hier testen aber einfach schon mal hier returnen und dann würden wir hier nochmal nur die Count-Funktion benchmarken das hier wollte ich und dann sehen wir jetzt hier ok, das Profil hat sich deutlich geändert wir haben jetzt hier immer noch ein bisschen Zeug, aber hauptsächlich ist dieser Block hier der teure Block nämlich die Decompression der G-SIP Packages weil das einzige was wir jetzt machen ist die Pakete runterladen das ist uns jetzt erstmal genug Beweise dafür, dass das Paasen ineffizient ist eine Sache möchte ich noch zeigen weil man das darin sehr gut sieht wobei ich dafür nochmal das Profil laufen lassen muss eine Sekunde ok, komm so und das machen wir groß so das war nicht ganz was ich wollte Moment, woran liegt das ah, ListCount klein geschrieben so, jetzt sehen wir alle Funktionen die irgendwas mit Count zu tun haben und hier sehen wir jetzt relativ klar auch nochmal die CPU-Zeiten das heißt wir können jetzt hier Zeile für Zeile wirklich schauen aha, ok die Verwendung der Map hier kostet uns 140 Millisekunden die Tempfeil runterladen vom Debian Mirror kostet uns 3 Sekunden aber hier das Paasen kostet uns 12 Sekunden das ist also das was wir auf jeden Fall eliminieren sollten und da machen wir uns doch jetzt mal dran eine Sache die wir jetzt noch machen ist wir testen beziehungsweise wir messen mit Time die Zeit damit wir auch einen ganz klaren Vorhernacherwert haben und da sollten wir jetzt also was ich jetzt hier gemacht habe ist statt Go Run, verwende ich jetzt Go Build damit das Kompellieren nicht in der Messung drin ist und dann messe ich einfach einmal den sauberen Programm Durchlauf und wir sehen jetzt hier, dass das 6,9 Sekunden gedauert hat und jetzt machen wir doch mal einen einfacheren Paaser wir sagen jetzt buffio.newscanner und lassen den mal hier durchscannen behandeln den Fehler den er zurückgeben kann und für jede Zeile die der jetzt liest denn die Dateiformate sind Zeilen basiert nehmen wir uns hier diesen Scene Quelltext und sagen erstmal strings has prefix scanner.txt Package, Call und Leerzeichen ist das was uns interessiert wenn das nicht gefunden wird überspringen und dann sagen wir hier strings.trim prefix scanner.txt Package, Call und Leerzeichen und dann lassen wir das Ganze doch mal laufen, schauen ob immer noch das gleiche Ergebnis bei rauskommt tada, gleiches Ergebnis und nur noch 2 Sekunden das Pausing war also wirklich furchtbar ineffizient und so haben wir es jetzt deutlich schneller gemacht nur um das hier auch noch mal zu illustrieren jetzt sehen wir hier ok 10 Megabyte 117 aber CPU ist deutlich weniger gut damit wollte ich jetzt illustrieren wie einfach das eigentlich ist Go-Programme zu profilen einfach mit den Boardmitteln und wie viel Spaß das eigentlich machen kann und wie viel entsprechende Ressourcen hat also eine ordentliche Netzwerkverbindung und viel CPU Kraft und dann schauen kann ok wenn ich das Programm jetzt ausführte wie schnell kann ich es wirklich machen und das innerhalb von 20 Minuten Live-Demo hier oben also das ist wirklich kinderleicht und ich hoffe das hat euch genauso viel Spaß gemacht wie mehr wie geht das Ganze jetzt weiter offensichtlich habe ich euch jetzt nicht viel über die Sprache erklärt ihr habt jetzt nicht super viel gelernt ihr könntet jetzt wahrscheinlich nicht eigene Programme schreiben aber ihr habt einen guten Eindruck davon dann schaut euch doch die Links an die ich auf diese Folie hier gepackt habe die Folien findet ihr gleich auch im Vortragssystem und unter den Beginners Ressources das sind die Ressources die Leuten im GoLang Slack Channel entgegen geschmissen bekommen wenn sie da join gibt es eine Liste an Dokumenten die ihr einfach durchlesen könnt da ist eine interaktive Tour durch die Go-Programmiersprache dabei in der ihr im Web Browser ohne irgendwas zu installieren einfach die ganzen Aufgaben erledigen könnt und immer auch gesagt bekommt ob das jetzt richtig war oder nicht und dann gibt es einen Guide das nennt sich Effective Go da gibt es so ein paar Tipps und Tricks wie man Go normalerweise verwendet und dann wird es schrittweise schwieriger also bis hin zu der Language Spec die im Vergleich zu C++ man sich durch das durchlesen kann andere Sprachen haben einen Sprachenstandortisierungskomitee im Sinne aber Go scheint wirklich den Programmierer im Sinne zu haben das heißt die Spec kann man sich auch in irgendwie zwei oder drei Stunden mal durchlesen wenn es ein wirklich interessiert danach könnt ihr euch auf golleng.org-slash-help in die Community verwenden dort gibt es einen Reddit-Forum es gibt irgendwie Mailing-Listen es gibt Twitter-Hashtags es gibt was auch immer ihr wollt es wird Leute geben die euch gerne helfen und zu guter letztlich hatte vorhin noch gemeint dass mit dem Go-Workspace mit dem Go-Path ist in Zukunft nicht mehr nötig und das liegt an einer größeren Änderung die hoffentliche über die nächsten Go-Versionen kommt und das nennt sich Version Go wie die Go-Community im Moment so größere Probleme behebt und wie das so aussieht so, jetzt bin ich hier am Ende der Folien angekommen ich hatte schon gesagt, wir haben jetzt gleich noch ein bisschen Zeit für Fragen ich möchte euch auch alle herzlich einladen wenn ihr schon ein bisschen mehr mit Go gemacht habt oder euch jetzt einfach das interessiert und ihr noch Fragen habt die ihr in nicht so einer formellen Runde klären wollt könnt ihr sich auch gerne draußen auf der Wiese noch ein bisschen unterhalten da findet ihr mich dann später und auch sonst auf der kompletten Konferenz natürlich mit euch über Go zu sprechen wenn euch irgendwas am Herzen liegt und zu guter Letzt möchte ich euch noch bitten mehr Feedback für diesen Vortrag zu geben das mit den vielen Live-Demos ist auch für mich ein bisschen was Neues ich hab hier den QR Code falls ihr das jetzt gleich machen wollt ansonsten könnt ihr auch einfach den Link klicken später in den Folien Gut, dann bevor wir die Fragerunde einleiten bleibt mir noch zu sagen vielen Dank für ihre Aufmerksamkeit Mikrofon haben wir hier vorne nicht? Genau, wir haben ein Mikrofon die Möglichkeit die Dateien auch zu optimieren wenn die gebraut sind dass die nicht 2 MB groß sind für den Hello World Ja gibt es, der Grund warum sie 2 MB groß sind für den Hello World ist eben dass das Filmpackage was wir benutzt haben für Hello World ausgeben relativ umfangreich ist weil sich das eben auch mit Unicode befassen muss und so weiter und so weiter und Go wird eben statisch kompelliert standardmäßig man kann es aber auch wenn man wirklich möchte dynamisch kompellieren lassen mit dem GCC Go Compiler und du kannst auch die Debug Infos abschalten und lauter so Zeug um die Binarys zu strippen und dann kommst du auch auf deutlich geringere Binarys Ja, hier vorne Wie würde man das machen wenn man jetzt den Code quasi teilen möchte und dann habe ich ja erstmal die Pakete gar nicht bei mir drauf gibt es sowas wie Maven oder NPM oder sowas in der Richtung Ja, also wenn du diesen Code jetzt teilen wollen würdest ich kann das auch einfach mal schnell zeigen Wir gehen jetzt hier nochmal in das Go Source Verzeichnis Wir haben jetzt hier ja dieses Hello Verzeichnis und wie ich das machen würde ist ich würde sagen ich mache jetzt hier github.com das gibt schon, dann Stapleberg für meinen Nutzernamen und dann würde ich das Hello Verzeichnis hier rein verschieben und dann dann würde ich auf github ein passendes Repository anlegen namens Hello und würde das einfach pushen, so weit so gut und wenn andere Leute dann dieses Programm sich runterladen möchten dann würden sie eben Go get und dann diesen Pfad sagen und wie ich vorhin schon gezeigt hatte in der Demo, wenn man Go get verwendet dann lädt das sich automatisch alle dependencies runter das heißt du musst bei Go nichts extra machen du musst nicht irgendwie zusätzliche Metadaten anlegen um zu sagen welche dependencies du verwendest sondern der liest das alles aus dem Source Code raus und macht dann automatisch was er machen muss denn es gibt also kein außer dem GCC kein dynamisches Linken aber wie ist das denn bei großen Projekten wenn ich das richtig verstanden habe, kompaliert Go dann auch immer alle dependencies mit ne das ist nicht korrekt sondern Packages werden einmal kompelliert und dann erst wieder wenn sie wirklich neu kompelliert werden müssen das kann ich auch noch schnell zeigen hier in dem Go Verzeichnis befindet sich Bin, Package und Source in Bin finden wir alle Sachen, die wir uns installiert haben in Source und gezeigten Source Code und in Package finden wir jetzt für unsere Architektur alles was der Go Compiler kompelliert hat schauen wir mal hier das Go das ist jetzt das Compilat und das würde jetzt nicht nochmal anfassen sieht man auch an dem Zeitstempel, das heißt um 1758 hat er das einmal kompelliert und dann hat er das immer wieder verwendet Ach so, also er lädt die Beine wie es nicht runter, aber wenn man sie einmal kompelliert hat hat er sie quasi in einem Cache drin das ist korrekt ja mittlerweile ist es übrigens auch so dass die Testausführungen gecached werden das heißt wenn sich an dem Testcode nichts geändert hat und an Independence nichts geändert hat, dann siehst du einfach die gleiche Ausgabe nochmal und er sagt ja dann aber auch wie du es nochmal laufen lassen könntest wenn du wirklich wolltest ja hier vorne war noch irgendwo eine Frage gerade eben vielleicht auch nicht ja hier vorne magst du nochmal ein bisschen was zu den Änderungen die jetzt mit Vigo oder zugehörigen jetzt noch kommen werden was darüber erzählen ja also ich kann es ganz kurz umreißen die Sache ist eben dass das Go-Get-Tool welches es seit vielen Jahren schon gibt also das integrale Bestandteil von Go bis bis jetzt eigentlich nichts von Versionen weiß, der lädt sich einfach immer Git hat runter also die ganz aktuell neueste Änderungen in dem Git Repository oder in dem was auch immer Repository in dem der source code liegt und das ist natürlich eine schlechte Idee gerade auch in größeren Projekten weil Versionsnummern eben doch wichtig sind um herauszufinden wo kam jetzt eine fehlhafte Änderung rein oder so und die Idee ist jetzt eben dass man Text verwendet die Semantic Versionen befolgen und dass das Go-Get-Tool diese Text beachtet und dann führte einen Algorithmus aus der nennt sich Minimum Version Selection indem er die Mindest-Version nimmt die zum Kompellieren nötig ist das ist ein bisschen was anderes als das was die meisten anderen Tools machen unter anderem eben auch Package Manager wenn man das Welt findet z.B. Deb was der bekannteste Package Manager ist die würden jeweils die neueste Version nehmen und nicht die älteste Version die noch funktioniert der Grund warum das gemacht wird ist allerdings vielfältig und das kann man sich in dem Artikel genau nachlesen aber das hat eben mehrere interessante Eigenschaften unter anderem dass du nicht mehr um die Dependencies zu Resolve einen kompletten Set Solver brauchst sondern dass der Algorithmus eben sehr simpel ist und sehr schnell terminiert unter anderem hat aber auch Community technische Auswirkungen die ganz spannend sind der erste Einwand den die meisten Leute bringen aber was ist denn mit Security Updates und dafür könnte man sagen ok die Patch Version sollen natürlich schon immer die höchsten genommen werden unter der Annahme dass sich da nichts ändert aber für die Major und Minor sollte man eben die niedrigste verwenden das ist auch generell ein interessanter Themenkomplex über den wir auch gerne noch später draußen treten können aber das ist glaube ich so das wichtigste weitere Fragen Mikro du hattest ja die Package Manager und du hast es schon mal ein bisschen politischer angesprochen also ich kenne jetzt gar nicht wirklich aber ich habe immer nur gehört dass halt alles über Git Repositories läuft und dann hat man eben genau dieses Versenierungsproblem und hat irgendwie keine Sicherheit sozusagen dass das irgendwie ein stabiles Dependency Set ist und da gibt es aber mehrere also mehrere Manager dafür das schaffe ich nochmal kurz was das Problem ist natürlich nicht super neu es ist uns nicht gerade eben erst aufgefallen dass diese Versionen vielleicht sinnvoll wären und der alte Workaround sozusagen für das Problem ist eben das sogenannte Wendoring also das technische Begriff dafür dass man eben die Libraries die man verwendet in sein eigenes Repository kopiert das ist jetzt nicht furchtbar schön aber das funktioniert eben also du kriegst halt dadurch einen reproduzierbaren Bild hin und was die allermeisten Package Manager für Go einfach machen ist dass sie eine Metadaten Datei haben ein Lockfile oder wie auch immer man das nennen möchte und daraus dann ein Wendoverzeichnis konstruieren mit dem Tool beim Kompilieren und Ausführen und so weiter auch berücksichtigt und über diesen Mechanismus funktionieren momentan die allermeisten Dependency Manager in der Zukunft ist das Wendoverzeichnis natürlich überflüssig mit WeGo hier vorne oder in der Mitte besser gesagt nee das war schon dokument oder gab es noch eine andere Frage ich benutze Debian und sehe halt in den Backpots es explodieren ständig die ganzen Go Packages die ganze per per Taarchiv einfach zu installieren alles komplett lokal im Hüßerverzeichnis zu machen gut ist in die Qualität der ganzen Packages also ich hab gesehen Go110 ist drin und hoffenweise Dependencies macht das Sinn einfach so einzusteigen kann ich dir beantworten die Compiler Packages aus der Linux-Distribution sind generell gut das heißt wenn du das bevorzugst die zu verwenden, dann kannst du die auch einfach verwenden es gibt ein Pferdefuß und der ist das im Moment es für Cross Compiler gegebenenfalls nötig ist dass du eine selbst installierte Version verwendest weil er eben Streibrechte in ein Verzeichnis braucht was wenn du es über den Paketmanager installierst du nicht hast aber dieser Back am Rande wenn dir das egal ist, also wenn du nur lokal entwickeln willst dann kannst du dir den Compiler einfach sagen wir aus Debian installieren die Dependencies allerdings, also die ganzen Packages die sich in Debian befinden, sind nicht in Debian damit Endnutzer sie verwenden können sondern ausschließlich damit Debian Software kompiliert werden kann das heißt selbst wenn du die installieren würdest würde das Go Tool die nicht finden deswegen brauchst du sie auch gar nicht erst installieren und die werden auch nicht Maintain für Endnutzer das heißt den Go Compiler und das Go Tool kannst du dir einfach über dein Distro Packagemanager installieren aber ab dann musst du halt eigentlich eh dieses Tool verwenden um dir die Packages und Programme runterzuladen ich überlasse es jetzt deinem Geschmack ob du das lieber aus deiner Distro nimmst oder nicht spielt eigentlich keine große Rolle wenn du eine solche Update bekommst noch mehr Fragen ansonsten machen wir ein paar Minuten früher Schluss und wie gesagt wenn euch noch was einfällt bitte einfach gerne ansprechen danke