diff --git a/Programmierparadigmen.pdf b/Programmierparadigmen.pdf index 7080adf..ba0566f 100644 Binary files a/Programmierparadigmen.pdf and b/Programmierparadigmen.pdf differ diff --git a/Programmierparadigmen.tex b/Programmierparadigmen.tex index 5f8f797..a294fc5 100644 --- a/Programmierparadigmen.tex +++ b/Programmierparadigmen.tex @@ -89,538 +89,86 @@ \raggedright \footnotesize - - % multicol parameters - % These lengths are set only within the main columns - \setlength{\columnseprule}{0.25pt} - \setlength{\premulticols}{1pt} - \setlength{\postmulticols}{1pt} - \setlength{\multicolsep}{1pt} - \setlength{\columnsep}{2pt} - - \section{Einleitung} - \subsection{Was ist ein Paradigma?} + +% multicol parameters +% These lengths are set only within the main columns +\setlength{\columnseprule}{0.25pt} +\setlength{\premulticols}{1pt} +\setlength{\postmulticols}{1pt} +\setlength{\multicolsep}{1pt} +\setlength{\columnsep}{2pt} + +\section{Einleitung} +\paragraph{Was ist ein Paradigma?} +\begin{itemize*} + \item Paradigma - aus dem Altgriechischen Beispiel, Muster; Erzählung mit beispielhaftem Charakter (laut Duden) + \item Programmierparadigmen beschreiben grundsätzliche Arten wie Computer-Programme formuliert werden können + \item Programmiersprachen können einzelne oder viele Konzepte aufgreifen \begin{itemize*} - \item Paradigma - aus dem Altgriechischen Beispiel, Muster; Erzählung mit beispielhaftem Charakter (laut Duden) - \item Programmierparadigmen beschreiben grundsätzliche Arten wie Computer-Programme formuliert werden können - \item Programmiersprachen können einzelne oder viele Konzepte aufgreifen - \begin{itemize*} - \item Keine verbreitete Sprache greift alle behandelten Konzepte auf - \item Betrachtung unterschiedlicher Sprachen - \end{itemize*} - \item Ziel der Veranstaltung: Weiten der in Algorithmen und Programmierung eingeführten Sichten hin zu einem Werkzeugkoffer zur Lösung realer Probleme... + \item Keine verbreitete Sprache greift alle behandelten Konzepte auf + \item Betrachtung unterschiedlicher Sprachen \end{itemize*} - - \subsection{Warum unterschiedliche Paradigmen?} - Komplexität von Software schlecht beherrschbar - - \subsection{Was bedeutet das?} + \item Ziel der Veranstaltung: Weiten der in Algorithmen und Programmierung eingeführten Sichten hin zu einem Werkzeugkoffer zur Lösung realer Probleme... +\end{itemize*} + +\paragraph{Warum unterschiedliche Paradigmen?} +Komplexität von Software schlecht beherrschbar + +\paragraph{Was bedeutet das?} +\begin{itemize*} + \item Programmierer schreiben, testen und dokumentieren zwischen 325 und 750 Codezeilen pro Monat \begin{itemize*} - \item Programmierer schreiben, testen und dokumentieren zwischen 325 und 750 Codezeilen pro Monat - \begin{itemize*} - \item maximal 360.000 Zeilen in 40 Jahren! - \end{itemize*} - \item Komplexität muss verborgen werden, z.B. durch - \begin{itemize*} - \item Kapselung - \item Spezifische Spachkonstrukte, Domain Specific Languages - \item Ausdrucksstärkere Sprachen - \end{itemize*} - \item Entwicklung neuer Programmierparadigmen hilft Grenzen (ein wenig) zu verschieben - \item Theoretische Rahmenbedingungen (Turing-Mächtigkeit, Satz von Rice) behalten Gültigkeit! + \item maximal 360.000 Zeilen in 40 Jahren! \end{itemize*} - - \subsection{Welche Paradigmen existieren?} + \item Komplexität muss verborgen werden, z.B. durch \begin{itemize*} - \item Aus Vorlesung AuP: - \begin{itemize*} - \item Imperative Algorithmen - \item Applikative Algorithmen - \item Deduktive Algorithmen - \end{itemize*} - \item Aber Vielzahl weiterer Formen - \begin{itemize*} - \item teilweise ergänzend, unterschiedliche Kategorisierung möglich - \item Bsp: prozedural, deklarativ, objekt-orientiert, datenstromorientiert, parallele \& verteilte Programmierung... - \end{itemize*} - \item Teilweise unterschiedliche Bezeichnungen - \begin{itemize*} - \item Applikativ bzw. Funktional - \item Deduktiv bzw. Logisch - \end{itemize*} - \item Aktueller Trend: Multiparadigmen-Sprachen - \begin{itemize*} - \item Umsetzung unterschiedlichster Paradigmen in einer Sprache - \item Beispiele: Scala, neuere C++-Standards, ... - \end{itemize*} + \item Kapselung + \item Spezifische Spachkonstrukte, Domain Specific Languages + \item Ausdrucksstärkere Sprachen \end{itemize*} - + \item Entwicklung neuer Programmierparadigmen hilft Grenzen (ein wenig) zu verschieben + \item Theoretische Rahmenbedingungen (Turing-Mächtigkeit, Satz von Rice) behalten Gültigkeit! +\end{itemize*} + +\paragraph{Welche Paradigmen existieren?} +\begin{itemize*} + \item Grundlegend + \begin{itemize*} + \item Imperative Algorithmen + \item Applikative Algorithmen + \item Deduktive Algorithmen + \end{itemize*} + \item Aber Vielzahl weiterer Formen + \begin{itemize*} + \item teilweise ergänzend, unterschiedliche Kategorisierung möglich + \item Bsp: prozedural, deklarativ, objekt-orientiert, datenstromorientiert, parallele \& verteilte Programmierung... + \end{itemize*} + \item Teilweise unterschiedliche Bezeichnungen + \begin{itemize*} + \item Applikativ bzw. Funktional + \item Deduktiv bzw. Logisch + \end{itemize*} + \item Aktueller Trend: Multiparadigmen-Sprachen + \begin{itemize*} + \item Umsetzung unterschiedlichster Paradigmen in einer Sprache + \item Beispiele: Scala, neuere C++-Standards, ... + \end{itemize*} +\end{itemize*} + \section{Objektorientierung und weiterführende Konzepte} - -\subsection{Unit-Testing} -\begin{itemize} - \item reproduzierbar \& vollautomatisierbar - \item Wann immer Änderungen in komplexen Programmen vorgenommen werden, möglichst vollständiger Test, da Programmierer nicht mehr alles überblicken - \item Messung von Überdeckung (Coverage) in Bezug auf Anzahl Funktionen, Code-Zeilen oder Verzweigungen - \item Wenn ein Bug beim Testen oder Live-Betrieb auftritt $\rightarrow$ Schreiben eines zusätzlichen Tests, um Wiederauftreten zu erkennen -\end{itemize} - -Klasse -\begin{lstlisting}[ - language=java, - showspaces=false, - basicstyle=\ttfamily - ] -public class Multi { - int mul(int a, int b) { - return a * b; - } -} -\end{lstlisting} -Testklasse -\begin{lstlisting}[ - language=java, - showspaces=false, - basicstyle=\ttfamily - ] -import static org.junit.jupiter.api.Assertions.*; -class MultiTest { - @org.junit.jupiter.api.Test - void mul() { - Multi m = new Multi(); - assertEquals(m.mul(1,2), 2, "should work"); - assertEquals(m.mul(2,0), 1, "explodes"); - } -} -\end{lstlisting} - -Zwei wesentliche Möglichkeiten: -1. Individuelles Testen von Klassen: Vernachlässigt Zusammenspiel zwischen Klassen -2. Gemeinsames Testen von Klassen: Erfordert Eingreifen in gekapselte Funktionalitäten - - - -\subsection{Reflections} -Programm zur Laufzeit inspizieren oder verändern - -\begin{lstlisting}[ - language=java, - showspaces=false, - basicstyle=\ttfamily - ] -static class Foo { - private String h = "Hallo"; - public void greet() { System.out.println(h); } -} -public static void main(String[] args) { - Foo foo = new Foo(); - foo.greet(); - try { - Field f = foo.getClass().getDeclaredField("h"); - f.setAccessible(true); - f.set(foo, "Moin"); - } catch (Exception e) { - } - foo.greet(); -} -\end{lstlisting} -Annotationen erlauben Anmerkungen an Klassen \& Methoden, Beginnen mit @ -Einige wenige vordefinierte z.B. @Override - -Nachteile: -\begin{itemize} - \item Geringe Geschwindigkeit weil Zugriff über Programmcode erfolgt - \item Kapselung kann umgangen werden -\end{itemize} - - - - -\subsection{Assertions/Pre-/Postconditions/Invarianten} -An sinnvollen Stellen im Programmcode testen, ob Annahmen/Zusicherungen (Assertions) stimmen... - -Aber: Ausführungsgeschwindigkeit niedriger -\begin{itemize} - \item Zeitverlust stark abhängig von Programm/Programmiersprache - \item Verbreitetes Vorgehen: - \begin{itemize} - \item Aktivieren der Tests in UnitTests und Debug-Versionen - \item Deaktivieren in Releases - \end{itemize} - \item Wann Assertion hinzufügen? "Eigentlich"-Regel: beim Gedanken "eigentlich müsste hier ... gelten" hinzufügen - \item Benötigt spezielle "if"-Bedingung: assert - \begin{lstlisting}[ - language=java, - showspaces=false, - basicstyle=\ttfamily - ] -class Stack { - public void push(Object o) { - ... - assert empty() == false - } -} -\end{lstlisting} - \item Spezielle Assertions: Pre- \& Postconditions - \begin{itemize} - \item Methoden/Programmabschnitte testen Bedingung vor und nach Ausführung - \item Einige Sprachen bieten spezialisierte Befehle: requires und ensures - \end{itemize} - \item Bei OO-Programmierung sind Vor- und Nachbedingungen nur eingeschränkt sinnvoll - \begin{itemize} - \item Bedingungen oft besser auf Objekt-Ebene $\rightarrow$ interner Zustand - \end{itemize} -\end{itemize} - - -\subsection{Exceptions} -\begin{itemize} - \item Wie wird mit Fehlern umgegangen? - \item gut für Code-Komplexität: Fehlerprüfungen an zentralerer Stelle - \begin{itemize} - \item Abbrechen und Programm-Stack "abbauen" bis (zentralere) Fehlerbehandlung greift - \item Dabei Fehler sinnvoll gruppieren - \end{itemize} - \item Java (und viele mehr): try/catch/throw-Konstrukt - \begin{itemize} - \item throw übergibt ein Objekt vom Typ Throwable an Handler, dabei zwei Unterarten: - \item Error: Sollte nicht abgefangen werden z.B. Fehler im Byte-Code, Fehlgeschlagene Assertions - \item Exceptions: Programm muss Exception fangen oder in Methode vermerken, - \end{itemize} -\end{itemize} - -\begin{lstlisting}[ - language=java, - showspaces=false, - basicstyle=\ttfamily - ] -try { - dangerousFunction(); -} catch(Exception e) { - // handle exceptions - return; -} finally { - // release locks etc.. -} -\end{lstlisting} - -\subsection{Generizät von Datentypen} -(Typ-)Generizität: -\begin{itemize} - \item Anwendung einer Implementierung auf verschiedene Datentypen - \item Parametrisierung eines Software-Elementes (Methode, Datenstruktur, Klasse, ...) durch einen oder mehrere Typen -\end{itemize} - - -\paragraph{Grenzen von Typsubstitution} -Kann ein Objekt einer Oberklasse (eines Typs) durch ein Objekt seiner Unterklasse (Subtyps) ersetzt werden? - -\paragraph{Ko- und Kontravarianz} -Geg.: Ordnung von Datentypen von spezifisch $\rightarrow$ allgemeiner -Gleichzeitige Betrachtung einer Klassenhierarchie, die Datentypen verwendet -\begin{itemize} - \item Kovarianz: Erhaltung der Ordnung der Typen - \item Kontravarianz: Umkehrung der Ordnung - \item Invarianz: keines von beiden -\end{itemize} -In objektorientierten Programmiersprachen im Allgemeinen -\begin{itemize} - \item Kontravarianz: für Eingabeparameter - \item Kovarianz: für Rückgabewerte und Ausnahmen - \item Invarianz: für Ein\item und Ausgabeparameter -\end{itemize} - -\paragraph{Liskovsches Substitutionsprinzip (LSP)} -> Wenn es für jedes Objekt $o_1$ eines Typs S ein Objekt $o_2$ des Typs T gibt, so dass für alle Programme P, die mit Operationen von T definiert sind, das Verhalten von P unverändert bleibt, wenn $o_2$ durch $o_1$ ersetzt wird, dann ist S ein Subtyp von T. -Barbara Liskov, 1988 bzw. 1993 - -Typsicherheit -\begin{lstlisting}[ - language=java, - showspaces=false, - basicstyle=\ttfamily - ] -String s = GMethod.thisOrThat("Java", "C++"); -Integer i = GMethod.thisOrThat(new Integer(42), new Integer(23)); -\end{lstlisting} - -\paragraph{Generics in Java} -... - -\paragraph{Ziel des Kapitels} +\subsection{Ziel des Kapitels} Einführen von Mechanismen zur Handhabung von komplexeren Code -\begin{itemize} +\begin{itemize*} \item Systematisiertes \& schnelles Testen \item Inspektion/Veränderung von Code zur Laufzeit \item Zusichern von Bedingungen \item Fehlerbehandlung \item Typsicherheit \item Generische und wiederverwendbare Algorithmen -\end{itemize} - -\subsection{Objektorientierung am Beispiel C++} -\begin{itemize} - \item Ziel von C++: volle Kontrolle über Speicher \& Ausführungsreihenfolgen sowie skalierbarere Projekt-Größe - \item Kompiliert zu nativem Maschinencode und erlaubt genauere Aussagen über Speicher-, Cache- und Echtzeitverhalten - \item Viele Hochsprachenelemente - \item Jedoch kompromissloser Fokus Ausführungsgeschwindigkeit, d.h. - \begin{itemize} - \item Keine automatische Speicherverwaltung - \item Keine Initialisierung von Variablen (im Allgemeinen) - \item Kein Speicherschutz! - \item Dinge, die Zeit kosten, müssen im Allgemeinen erst durch Schlüsselworte aktiviert werden - \end{itemize} -\end{itemize} - -> C makes it easy to shoot yourself in the foot; C++ makes it harder, -but when you do it blows your whole leg off. -Bjarne Stroustrup - -\paragraph{Vergleich mit Java} -\begin{lstlisting}[ - language=java, - showspaces=false, - basicstyle=\ttfamily - ] -[Hello.java] -package hello; -public class Hello { - public static void main(String args[]){ - System.out.println("Hi Welt"); - } -} -\end{lstlisting} -\begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] -[Hello.cpp] -\#include -int main(int argc, char* argv[]){ - std::cout << "Hello World" << std::endl; - return 0; -} -\end{lstlisting} - -Wichtige Makrobefehle: -\begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] -\#include "X.hpp" // Datei X.hpp aus Projekt-Ordner -\#include // Datei cstdio aus System-Includes - -\#ifdef DEBUG // falls Konstante DEBUG definiert ist -std::cout << "Wichtige Debugausgabe" << std::endl; -\#endif - -\#define DEBUG // Konstante setzen -\#define VERSION 3.1415 // Konstante auf einen Wert setzen -\#define DPRINT(X) std::cout << X << std::endl; // Macro-Fkt. -\#undef DEBUG // Konstante löschen, good practice! - -\#ifndef __linux__ // falls nicht für Linux übersetzt -playMinesweeper(); -\#endif -\end{lstlisting} - -\paragraph{Speichermanagement} -Anlegen eines Objektes auf dem Heap: -\begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] -std::string* s = new std::string("wiz!"); -delete s; -\end{lstlisting} -Allokation von Feldern: -\begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] -int* i = new int[29]; // gültige Indicies 0-28 -i[0] = 23; -delete [] i; // nicht mit delete i; verwechseln! -\end{lstlisting} -Zeiger können durch \& auf beliebige Variablen ermittelt werden -\begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] -int i = 0; -int* j = \&i; // \&-Operator erzeugt Zeiger; j darf nicht gelöscht werden -\end{lstlisting} -Zeiger können durch * dereferenziert werden -\begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] -int i = 0; -int* j = \&i; // \&-Operator erzeugt Zeiger -*j = 1; // Zugriff auf Variableninhalt -\end{lstlisting} -Zugriff auf Methoden/Member Variablen -\begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] -std::string* s = new std::string("wiz"); -(*s).push_back('?'); // manuelles Derefenzieren -s$\rightarrow$push_back('?'); // $\rightarrow$ Operator -delete s; -\end{lstlisting} -Zeiger können verwendet werden, um schreibend zuzugreifen -\begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] -void set(std::string* s) { *s = "foo"; } -int main() { - std::string s = "bar"; - set(\&s); - std::cout << s; // gibt foo aus - return 0; -} -\end{lstlisting} - -\paragraph{Vererbung} -Definierte Programmierschnittstellen durch Überschreiben von Methoden/abstrakte Methoden; Vermeiden von Dopplung interner Daten. -Unterschied zu Java: Methoden "liegen" bei C++ statisch im Speicher - -\begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] -class Stromfresser { -public: - Stromfresser() { - std::cerr << "Mjam" << std::endl; - } -}; -class Roboter : virtual public Stromfresser {}; -class Staubsauger : virtual public Stromfresser {}; -class Roomba : public Staubsauger, public Roboter {}; - -int main() { - Roomba q; - return 0; -} -\end{lstlisting} -Mit virtual: "Mjam", ohne virtual: "Mjam Mjam" - - -\paragraph{Mehrfachvererbung} - - -\paragraph{Operator-Overloading} -\begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] -class MagicString { // Remember me? - std::string* s; -public: - MagicString() : s(new std::string("wiz!")) {} - MagicString(const MagicString\& m) : s(new std::string(*m.s)) {} - std::string* magicStr() { return s; } - // Neu: = operator erlaubt Zuweisungen - MagicString\& operator=(const MagicString\& other) { - if(this != \&other) { - // ACHTUNG beide Werte werden dereferenziert... - // ruft operator= in std::string auf $\rightarrow$ String wird kopiert - *s = *other.s; - } - return *this; - } - ~MagicString() { delete s; } -}; -\end{lstlisting} - - -\paragraph{Templates} -\begin{itemize} - \item Generische Datentypen werden in C++ mit Templates realsiert - \item Häufig ähnlich eingesetzt wie Generics, aber können neben Typen auch Konstanten enthalten - \item Zur Compile-Zeit aufgelöst $\rightarrow$ Deklaration \& Implementierung in Header-Dateien - \item Einfaches Beispiel (mit Typen, ähnl. zu Generics, primitive Typen ok!): -\end{itemize} - -\paragraph{Container} -\begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] -template class vector; // dynamisches Array -template class list; // doppelt verkette Liste -template class set; // geordnete Menge basiert auf Baum -template class map; // Assoziatives Array, -\end{lstlisting} - - -\paragraph{Shared Pointer} -\begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] -template class shared_ptr { // Vereinfacht! - T* p; // Zeiger auf eigentliches Objekt - int*r; // Referenzzähler -public: - // neue Referenz auf Objekt erzeugen - shared_ptr(T* t) : p(t), r(new int) { *r = 1; } - // Referenz durch andere Referenz erzeugen - shared_ptr(const shared_ptr\& sp) : p(sp.p), r(sp.r) { ++(*r); } - T* operator$\rightarrow$() const { // benutzen wie einen richtigen Zeiger - return p; - } - ~shared_ptr() { - if(--(*r) == 0) { // Objekt loeschen, wenn letzte Referenz weg - delete r; - delete p; -}}}; -\end{lstlisting} - -\paragraph{Zusammenfassung} -\begin{itemize} - \item C++ erlaubt sehr detaillierte Kontrolle über Speicher- und Laufzeitverhalten - \item Es ist relativ einfach, schwierig zu findende Fehler einzubauen - \item Die Sprache ist durch Operator-Overloading, Mehrfachvererbung und Templates sehr mächtig - \item Erlaubt hohen Grad an Wiederverwendung - \item Anzahl an Code-Zeilen kann reduziert werden - \item Code kann völlig unlesbar werden! Viele Features sollten nur eingesetzt werden wenn sich dadurch ein wirklicher Vorteil ergibt! -\end{itemize} - - -\subsection{Objektorientierung am Beispiel Java} -\begin{itemize*} - \item Bekannt: - \begin{itemize*} - \item Grundlegendes Verständnis von Java - \item Kapselung durch Klassen und Vererbung - \end{itemize*} - \item Ziele: - \begin{itemize*} - \item Verständnis der Probleme bei Vererbung und Typersetzbarkeit in objektorientierten Programmiersprachen - \item Kennenlernen der Grundideen generischer und abstrahierender Konzepte in Objekt-orientierter Programmierung (OOP) - \item Praktische Erfahrungen anhand von Java \& C++ - \end{itemize*} - \item Ausdrucksstärke erhöhen, Komplexität verbergen \end{itemize*} -\subsubsection{Unit Testing} +\subsection{Unit-Testing} \paragraph{Motivation} \begin{itemize*} \item Große Software-Systeme entwickeln sich über lange Zeiträume @@ -654,21 +202,14 @@ public: \item Gute Praxis: Wenn ein Bug beim Testen oder Live-Betrieb auftritt -> Schreiben eines zusätzlichen Tests, um Wiederauftreten zu erkennen \end{itemize*} -\paragraph{Unit-Testing in Java} \begin{itemize*} - \item De facto Standard: JUnit Framework - \item "Best Practice" für einfachen Einsatz: - \begin{itemize*} - \item Java Code in ein oder mehrere Klassen im Ordner src speichern - \item Im Ordner tests jeweils eine Klasse anlegen, die Funktionen einer Implementierungsklasse prüft - \item Konvention: Testklasse einer Klasse Name heißt NameTest - \item Eigentliche Tests werden in Methoden implementiert, die als Tests annotiert sind - \item Typischer Ansatz: für bekannte Werte ausführen und Ergebnis mit Grundwahrheit (erwartetes Verhalten) vergleichen, bspw. mit assertEquals-Funktion - \end{itemize*} - \item Viele weitere Features, z.B. Deaktivieren von Tests, Timeouts, GUI Coverage, Mocks + \item reproduzierbar \& vollautomatisierbar + \item Wann immer Änderungen in komplexen Programmen vorgenommen werden, möglichst vollständiger Test, da Programmierer nicht mehr alles überblicken + \item Messung von Überdeckung (Coverage) in Bezug auf Anzahl Funktionen, Code-Zeilen oder Verzweigungen + \item Wenn ein Bug beim Testen oder Live-Betrieb auftritt $\rightarrow$ Schreiben eines zusätzlichen Tests, um Wiederauftreten zu erkennen \end{itemize*} -\paragraph{Unit Testing - Richtiges Abstraktionsniveau} +\paragraph{Richtiges Abstraktionsniveau bei Unit Testing} \begin{itemize*} \item Um die Tests auszuführen, müssen jeweils entsprechende Hauptprogramme generiert werden ("Test Suites") \item Hauptschwierigkeiten von Unit-Tests: @@ -693,7 +234,44 @@ public: \end{itemize*} \end{itemize*} -\subsubsection{Reflections} +Zwei wesentliche Möglichkeiten: +\begin{itemize*} + \item Individuelles Testen von Klassen: Vernachlässigt Zusammenspiel zwischen Klassen + \item Gemeinsames Testen von Klassen: Erfordert Eingreifen in gekapselte Funktionalitäten +\end{itemize*} + +Klasse +\begin{lstlisting}[ + language=java, + showspaces=false, + basicstyle=\ttfamily + ] +public class Multi { + int mul(int a, int b) { + return a * b; + } +} +\end{lstlisting} + +Testklasse +\begin{lstlisting}[ + language=java, + showspaces=false, + basicstyle=\ttfamily + ] +import static org.junit.jupiter.api.Assertions.*; +class MultiTest { + @org.junit.jupiter.api.Test + void mul() { + Multi m = new Multi(); + assertEquals(m.mul(1,2), 2, "should work"); + assertEquals(m.mul(2,0), 1, "explodes"); + } +} +\end{lstlisting} + +\subsection{Reflections} + \begin{itemize*} \item Normaler Ablauf: Programm schreiben, compilieren, ausführen \begin{itemize*} @@ -738,6 +316,31 @@ for (Method m : methods) System.out.println(m.getName()); \end{lstlisting} +Kurz: Programm zur Laufzeit inspizieren oder verändern + +\begin{lstlisting}[ + language=java, + showspaces=false, + basicstyle=\ttfamily + ] +static class Foo { + private String h = "Hallo"; + public void greet() { System.out.println(h); } +} +public static void main(String[] args) { + Foo foo = new Foo(); + foo.greet(); + try { + Field f = foo.getClass().getDeclaredField("h"); + f.setAccessible(true); + f.set(foo, "Moin"); + } catch (Exception e) { + } + foo.greet(); +} +\end{lstlisting} + + \paragraph{Annotationen} \begin{itemize*} \item Annotationen erlauben Anmerkungen an Klassen \& Methoden @@ -758,6 +361,15 @@ class MultiTest { void mul() { ... \end{lstlisting} +Annotationen erlauben Anmerkungen an Klassen \& Methoden, Beginnen mit @ +Einige wenige vordefinierte z.B. @Override + +Nachteile: +\begin{itemize*} + \item Geringe Geschwindigkeit weil Zugriff über Programmcode erfolgt + \item Kapselung kann umgangen werden +\end{itemize*} + \paragraph{Reflektionen über Reflections} @@ -783,7 +395,8 @@ void mul() { \end{itemize*} -\subsubsection{Assertions} + +\subsection{Assertions/Pre-/Postconditions/Invarianten} \begin{itemize*} \item Kann man interne Zustände testen, ohne invasive Techniken wie Reflections? \item Einfache Möglichkeit: An sinnvollen Stellen im Programmcode testen, ob Annahmen/Zusicherungen (Assertions) stimmen... @@ -811,26 +424,30 @@ public void push(Object o) { \end{lstlisting} + +An sinnvollen Stellen im Programmcode testen, ob Annahmen/Zusicherungen (Assertions) stimmen... + Aber: Ausführungsgeschwindigkeit niedriger \begin{itemize*} - \item Zeitverlust stark abhängig von Programm/Programmiersprache - \item Verbreitetes Vorgehen: - \begin{itemize*} - \item Aktivieren der Tests in UnitTests und Debug-Versionen - \item Deaktivieren in Releases - \item Benötigt spezielle "if"-Bedingung: assert - \end{itemize*} - \item Aktivierung der Tests über Start mit java -ea +\item Zeitverlust stark abhängig von Programm/Programmiersprache +\item Verbreitetes Vorgehen: +\begin{itemize*} + \item Aktivieren der Tests in UnitTests und Debug-Versionen + \item Deaktivieren in Releases \end{itemize*} +\item Wann Assertion hinzufügen? "Eigentlich"-Regel: beim Gedanken "eigentlich müsste hier ... gelten" hinzufügen +\item Aktivierung der Tests über Start mit java -ea +\item Benötigt spezielle "if"-Bedingung: assert \begin{lstlisting}[ - language=java, - showspaces=false, - basicstyle=\ttfamily - ] + language=java, + showspaces=false, + basicstyle=\ttfamily + ] class Stack { -public void push(Object o) { - ... - assert empty() == false + public void push(Object o) { + ... + assert empty() == false + } } \end{lstlisting} @@ -847,6 +464,12 @@ public void push(Object o) { \paragraph{Spezielle Assertions: Pre- \& Postconditions} \begin{itemize*} + \item Methoden/Programmabschnitte testen Bedingung vor und nach Ausführung + \item Einige Sprachen bieten spezialisierte Befehle: requires und ensures + \item Bei OO-Programmierung sind Vor- und Nachbedingungen nur eingeschränkt sinnvoll + \begin{itemize*} + \item Bedingungen oft besser auf Objekt-Ebene $\rightarrow$ interner Zustand + \end{itemize*} \item An welchen Stellen ist es sinnvoll, Annahmen zu prüfen? \item Einfache Antwort: an so vielen Stellen wie möglich \item Komplexere Antwort: Design by contract, ursprünglich Eiffel @@ -870,7 +493,6 @@ public void push(Object o) { } \end{lstlisting} - \paragraph{Klasseninvarianten} \begin{itemize*} \item Bei OO-Programmierung sind Vor- und Nachbedingungen nur eingeschränkt sinnvoll @@ -901,7 +523,8 @@ public void push(Object o) { \end{lstlisting} -\subsubsection{Exeptions} +\subsection{Exceptions} + Signifikantes Element vieler Sprachen: Wie wird mit Fehlern umgegangen? Fehler können unterschiedliche Gründe haben Besser für Code-Komplexität: Fehlerprüfungen an zentralerer Stelle @@ -910,23 +533,40 @@ Besser für Code-Komplexität: Fehlerprüfungen an zentralerer Stelle \item Dabei Fehler sinnvoll gruppieren \item Java (und viele mehr): try/catch/throw-Konstrukt \end{itemize*} + +\begin{itemize*} + \item Wie wird mit Fehlern umgegangen? + \item gut für Code-Komplexität: Fehlerprüfungen an zentralerer Stelle + \begin{itemize*} + \item Abbrechen und Programm-Stack "abbauen" bis (zentralere) Fehlerbehandlung greift + \item Dabei Fehler sinnvoll gruppieren + \end{itemize*} + \item Java (und viele mehr): try/catch/throw-Konstrukt + \begin{itemize*} + \item throw übergibt ein Objekt vom Typ Throwable an Handler, dabei zwei Unterarten: + \item Error: Sollte nicht abgefangen werden z.B. Fehler im Byte-Code, Fehlgeschlagene Assertions + \item Exceptions: Programm muss Exception fangen oder in Methode vermerken, + \end{itemize*} +\end{itemize*} + \begin{lstlisting}[ - language=java, - showspaces=false, - basicstyle=\ttfamily - ] + language=java, + showspaces=false, + basicstyle=\ttfamily +] private void readFile(String f) { try { - Path file = Paths.get("/tmp/file"); - if(Files.exists(file) == false) - throw new IOException("No such dir"); - array = Files.readAllBytes(file); +Path file = Paths.get("/tmp/file"); +if(Files.exists(file) == false) + throw new IOException("No such dir"); +array = Files.readAllBytes(file); } catch(IOException e) { - // do something about it +// do something about it } } \end{lstlisting} + throw übergibt ein Objekt vom Typ Throwable an Handler, dabei zwei Unterarten: \begin{itemize*} \item Error: Sollte nicht abgefangen werden z.B. Fehler im Byte-Code, Fehlgeschlagene Assertions @@ -979,24 +619,22 @@ dangerousFunction(); Aufräumen nach einem try-catch-Block: Anweisungen im finally-Block werden immer ausgeführt, d.h. auch bei return in try- oder catch-Block (oder fehlerloser Ausführung) \begin{lstlisting}[ - language=java, - showspaces=false, - basicstyle=\ttfamily - ] + language=java, + showspaces=false, + basicstyle=\ttfamily + ] try { -dangerousFunction(); + dangerousFunction(); } catch(Exception e) { -// handle exceptions -return; + // handle exceptions + return; } finally { -// release locks etc.. + // release locks etc.. } \end{lstlisting} - - -\subsubsection{Generizät von Datentypen} -(Typ-)Generizität: +\subsection{Generizät von Datentypen} +(Typ-)Generizität: \begin{itemize*} \item Anwendung einer Implementierung auf verschiedene Datentypen \item Parametrisierung eines Software-Elementes (Methode, Datenstruktur, Klasse, ...) durch einen oder mehrere Typen @@ -1020,7 +658,8 @@ return a.compareTo(b) < 0 ? a : b; \paragraph{Grenzen von Typsubstitution} -Problem: Für jeden Typ? Wie kann sort implementiert werden? +Kann ein Objekt einer Oberklasse (eines Typs) durch ein Objekt seiner Unterklasse (Subtyps) ersetzt werden? + Möglicher Ausweg: Klassenhierarchie mit zentraler Basisklasse \begin{lstlisting}[ language=java, @@ -1076,20 +715,26 @@ c.skaliereY(.5); //is das noch ein Kreis? evtl. Reihenfolge in der Klassenhierarchie tauschen (nutzung von Radius)? Was bedeutet das für Ellipse? Verwandte Probleme: Rechteck-Quadrat, Set-Bag -\paragraph{Ko- und Kontravarianz} +\subsubsection{Ko- und Kontravarianz} Geg.: Ordnung von Datentypen von spezifisch $\rightarrow$ allgemeiner +Gleichzeitige Betrachtung einer Klassenhierarchie, die Datentypen verwendet \begin{itemize*} - \item Gleichzeitige Betrachtung einer Klassenhierarchie, die Datentypen verwendet \item Kovarianz: Erhaltung der Ordnung der Typen \item Kontravarianz: Umkehrung der Ordnung \item Invarianz: keines von beiden - \item Anwendung für - \begin{itemize*} - \item Parameter - \item Rückgabetypen - \item Ausnahmetypen - \item Generische Datenstrukturen - \end{itemize*} +\end{itemize*} +In objektorientierten Programmiersprachen im Allgemeinen +\begin{itemize*} + \item Kontravarianz: für Eingabeparameter + \item Kovarianz: für Rückgabewerte und Ausnahmen + \item Invarianz: für Ein- und Ausgabeparameter +\end{itemize*} +Anwendung für +\begin{itemize*} + \item Parameter + \item Rückgabetypen + \item Ausnahmetypen + \item Generische Datenstrukturen \end{itemize*} Beispiel: Basierend auf Meyer‘s SKIER-Szenario @@ -1181,12 +826,23 @@ In objektorientierten Programmiersprachen im Allgemeinen \item Invarianz: für Ein- und Ausgabeparameter \end{itemize*} -\paragraph{Liskovsches Substitutionsprinzip (LSP)} +\subsection{Liskovsches Substitutionsprinzip (LSP)} Barbara Liskov, 1988 bzw. 1993, definiert stärkere Form der Subtyp-Relation, berücksichtigt Verhalten: > Wenn es für jedes Objekt $o_1$ eines Typs S ein Objekt $o_2$ des Typs T gibt, so dass für alle Programme P, die mit Operationen von T definiert sind, das Verhalten von P unverändert bleibt, wenn $o_2$ durch $o_1$ ersetzt wird, dann ist S ein Subtyp von T.' Subtyp darf Funktionalität eines Basistyps nur erweitern, aber nicht einschränken.
Beispiel: Kreis-Ellipse $\rightarrow$ Kreis als Unterklasse schränkt Funktionalität ein und verletzt damit LSP +Typsicherheit +\begin{lstlisting}[ + language=java, + showspaces=false, + basicstyle=\ttfamily + ] +String s = GMethod.thisOrThat("Java", "C++"); +Integer i = GMethod.thisOrThat(new Integer(42), new Integer(23)); +\end{lstlisting} + + \paragraph{Generics in Java (Typsicherheit)} Motivation: Parametrisierung von Kollektionen mit Typen \begin{lstlisting}[ @@ -1350,7 +1006,7 @@ GArray anotherArray = (GArray) anArray; \end{lstlisting} -\paragraph{Wildcards} +\subsubsection{Wildcards} Wildcard "?" als Typparameter und abstrakter Supertyp für alle Instanziierungen \begin{lstlisting}[ language=java, @@ -1443,11 +1099,11 @@ Object obj = array.get(0); PECS = Producer extends, Consumer super $\rightarrow$ Producer liest nur sachen, Consumer legt daten/Objekte/... ab -\subsection{Objectorientierung am Beispiel C++} +\subsection{Objektorientierung am Beispiel C++} \begin{itemize*} \item Ziel von C++: volle Kontrolle über Speicher \& Ausführungsreihenfolgen sowie skalierbarere Projekt-Größe \item Kompiliert zu nativem Maschinencode und erlaubt genauere Aussagen über Speicher-, Cache- und Echtzeitverhalten - \item Viele Hochsprachenelemente (Wie Java objektorientiert; sogar ähnliche Syntax an viele Stellen (weil Java ursprünglich an C++ angelehnt)) + \item Viele Hochsprachenelemente \item Jedoch kompromissloser Fokus Ausführungsgeschwindigkeit, d.h. \begin{itemize*} \item Keine automatische Speicherverwaltung @@ -1463,72 +1119,32 @@ PECS = Producer extends, Consumer super $\rightarrow$ Producer liest nur sachen, \end{itemize*} \end{itemize*} -\subsubsection{Vergleich mit Java} +> C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off. [Bjarne Stroustrup] + + +Wichtige Makrobefehle: \begin{lstlisting}[ - language=java, - showspaces=false, - basicstyle=\ttfamily - ] -[Hello.java] -package hello; // say that we are part of a package -public class Hello { // declare a class called Hello: -// declare the function main that takes an array of Strings: -public static void main(String args[]) { -// call the static method, println on class System.out with parameter "Hi Welt!": - System.out.println("Hi Welt!"); -} -} // end of class Hello + language=C++, + showspaces=false, + basicstyle=\ttfamily + ] +#include "X.hpp" // Datei X.hpp aus Projekt-Ordner +#include // Datei cstdio aus System-Includes + +#ifdef DEBUG // falls Konstante DEBUG definiert ist +std::cout << "Wichtige Debugausgabe" << std::endl; +#endif + +#define DEBUG // Konstante setzen +#define VERSION 3.1415 // Konstante auf einen Wert setzen +#define DPRINT(X) std::cout << X << std::endl; // Macro-Fkt. +#undef DEBUG // Konstante löschen, good practice! + +#ifndef __linux__ // falls nicht für Linux übersetzt +playMinesweeper(); +#endif \end{lstlisting} -\begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] -[Hello.cpp] -// include declarations for I/O library where cout object is specified in namespace std:: -\#include -// declare the function main that takes an int and array of strings and returns an int as the exit code -int main(int argc, char* argv[]) { -// stream string to cout object flush line with endl -std::cout << "Hello world!" -<< std::endl; -return 0; -} // end of main() -\end{lstlisting} - -\begin{itemize*} - \item Unterschiede im Aufbau: - \begin{itemize*} - \item C++ hat globale Funktionen, also außerhalb von Klassen, wie main - \item \#include gibt Dateien mit Klassen- und Funktionsdefinitionen an, die der Compiler einlesen soll - \item Java-Programme werden in packages gegliedert, in C++ gibt es mit modules ein ähnliches Konzept, welches aber (noch) nicht verbreitet ist - \item C++-Programme können (ohne Bezug zu Dateien) in namespaces untergliedert werden, hier std - \end{itemize*} - \item Programmargumente: - \begin{itemize*} - \item In Java bekommt main ein String-Array übergeben, die Länge kann über .length abgefragt werden - \item C/C++-Programme erhalten ein Array von char* (Details zu Pointern folgen) - \item In C/C++ sind Arrays keine Pseudoobjekte, sondern Speicherbereiche in denen die Daten konsekutiv abgelegt sind $\rightarrow$ argc wird benötigt die Anzahl an Elementen zu kodieren - \end{itemize*} - \item Rückgabewerte: - \begin{itemize*} - \item In Java keine Rückgabe in der main-Methode - \item In C++ Rückgabe eines exit code - \begin{itemize*} - \item 0 gibt an: Programmausführung erfolgreich - \item Andere Werte geben einen Programm-spezifischen Fehlercode zurück - \end{itemize*} - \end{itemize*} - \item Primitive Datentypen: - \begin{itemize*} - \item Wie in Java einfache Datentypen, die "Zahlen" enthalten - \item char, short, int, long sind auf 64-bit Maschinen 8 bit, 16 bit, 32 bit und 64 bit breit (char braucht in Java 16 Bit!) - \item long ist auf 32 bit Maschinen 32 Bit breit, long long [sic!] ist immer 64 Bit - \item bool speichert Boolsche Werte (Breite hängt vom Compiler ab!) - \item Ein unsigned vor Ganzahltypen gibt an, dass keine negativen Zahlen in der Variable gespeichert werden (Beispiel: unsigned int) $\rightarrow$ Kann größere Zahlen speichern \& zu viel Unsinn führen (beim Vergleich mit vorzeichenbehafteten Zahlen) - \end{itemize*} -\end{itemize*} \subsubsection{C++ Klassen} Header Foo.hpp deklariert Struktur und Schnittstelle @@ -1644,33 +1260,33 @@ Beispiele: showspaces=false, basicstyle=\ttfamily ] -\#include "X.hpp" // Datei X.hpp aus Projekt-Ordner -\#include // Datei cstdio aus System-Includes +#include "X.hpp" // Datei X.hpp aus Projekt-Ordner +#include // Datei cstdio aus System-Includes -\#ifdef DEBUG // falls Konstante DEBUG definiert ist +#ifdef DEBUG // falls Konstante DEBUG definiert ist std::cout << "Wichtige Debugausgabe" << std::endl; -\#endif +#endif -\#define DEBUG // Konstante setzen -\#define VERSION 3.1415 // Konstante auf einen Wert setzen -\#define DPRINT(X) std::cout << X << std::endl; // Macro-Fkt. -\#undef DEBUG // Konstante löschen, good practice! +#define DEBUG // Konstante setzen +#define VERSION 3.1415 // Konstante auf einen Wert setzen +#define DPRINT(X) std::cout << X << std::endl; // Macro-Fkt. +#undef DEBUG // Konstante löschen, good practice! -\#ifndef __linux__ // falls nicht für Linux übersetzt +#ifndef __linux__ // falls nicht für Linux übersetzt playMinesweeper(); -\#endif +#endif \end{lstlisting} -\paragraph{Einschub: Include Guards} +\subsubsection{Include Guards} Eine (oft hässliche) Eigenschaft des \#include-Befehls: kein Überprüfen ob eine Datei vorher bereits eingebunden wurde. Problematisches Beispiel: \begin{lstlisting}[ language=C++, showspaces=false, basicstyle=\ttfamily ] -\#include "Bar.hpp" //in "Bar.hpp" ist "Foo.hpp" bereits inkludiert worden -\#include "Foo.hpp" //Fehler weil kallse Foo bereits deklariert wurde +#include "Bar.hpp" //in "Bar.hpp" ist "Foo.hpp" bereits inkludiert worden +#include "Foo.hpp" //Fehler weil kallse Foo bereits deklariert wurde \end{lstlisting} Common Practice: Include-Guards um alle Header-Dateien @@ -1679,14 +1295,15 @@ Common Practice: Include-Guards um alle Header-Dateien showspaces=false, basicstyle=\ttfamily ] -\#ifndef FOO_HPP -\#define FOO_HPP +#ifndef FOO_HPP +#define FOO_HPP ... -\#endif +#endif \end{lstlisting} \subsubsection{Speichermanagement} + \begin{itemize*} \item Programmspeicher enthält Code und Daten, vom Betriebssystem i.A. auf virtuelle Adressbereiche abgebildet \item Unterschiedliche Varianten von Datenspeicher: @@ -1857,6 +1474,60 @@ Beispiele } \end{lstlisting} \end{itemize*} +Allokation von Feldern: +\begin{lstlisting}[ + language=C++, + showspaces=false, + basicstyle=\ttfamily + ] +int* i = new int[29]; // gültige Indicies 0-28 +i[0] = 23; +delete [] i; // nicht mit delete i; verwechseln! +\end{lstlisting} +Zeiger können durch \& auf beliebige Variablen ermittelt werden +\begin{lstlisting}[ + language=C++, + showspaces=false, + basicstyle=\ttfamily + ] +int i = 0; +int* j = \&i; // \&-Operator erzeugt Zeiger; j darf nicht gelöscht werden +\end{lstlisting} +Zeiger können durch * dereferenziert werden +\begin{lstlisting}[ + language=C++, + showspaces=false, + basicstyle=\ttfamily + ] +int i = 0; +int* j = \&i; // \&-Operator erzeugt Zeiger +*j = 1; // Zugriff auf Variableninhalt +\end{lstlisting} +Zugriff auf Methoden/Member Variablen +\begin{lstlisting}[ + language=C++, + showspaces=false, + basicstyle=\ttfamily + ] +std::string* s = new std::string("wiz"); +(*s).push_back('?'); // manuelles Derefenzieren +s$\rightarrow$push_back('?'); // $\rightarrow$ Operator +delete s; +\end{lstlisting} +Zeiger können verwendet werden, um schreibend zuzugreifen +\begin{lstlisting}[ + language=C++, + showspaces=false, + basicstyle=\ttfamily + ] +void set(std::string* s) { *s = "foo"; } +int main() { + std::string s = "bar"; + set(\&s); + std::cout << s; // gibt foo aus + return 0; +} +\end{lstlisting} Warum wirken sich Speicherfehler so unvorhersehbar aus? @@ -1988,7 +1659,6 @@ std::string* magicStr() { \end{itemize*} \end{itemize*} - \subsubsection{Vererbung} \begin{itemize*} \item Vermeiden von Mehrfachimplementierungen @@ -2045,8 +1715,34 @@ class FooBar : public Foo { \item override-Markierung optional, aber hätte vor fehlendem virtual gewarnt! \end{itemize*} \end{itemize*} +Definierte Programmierschnittstellen durch Überschreiben von Methoden/abstrakte Methoden; Vermeiden von Dopplung interner Daten. +Unterschied zu Java: Methoden "liegen" bei C++ statisch im Speicher + +\begin{lstlisting}[ + language=C++, + showspaces=false, + basicstyle=\ttfamily + ] +class Stromfresser { +public: + Stromfresser() { + std::cerr << "Mjam" << std::endl; + } +}; +class Roboter : virtual public Stromfresser {}; +class Staubsauger : virtual public Stromfresser {}; +class Roomba : public Staubsauger, public Roboter {}; + +int main() { + Roomba q; + return 0; +} +\end{lstlisting} +Mit virtual: "Mjam", ohne virtual: "Mjam Mjam" + + +\paragraph{Mehrfachvererbung} -\subsubsection{Mehrfachvererbung} \begin{itemize*} \item C++ unterstützt keine Interfaces \item Aber C++ unterstützt Mehrfachvererbung! Pro Interface eine Basisklasse -> mit abstrakten Methoden erstellen @@ -2110,8 +1806,8 @@ public: }; \end{lstlisting} +\subsubsection{Operator-Overloading} -\subsubsection{Operator Overloading} \begin{itemize*} \item In Java: Unterschied zwischen "==" und "equals()" bei String-Vergleich \item In C++: "=="-Operator für String-Vergleich @@ -2129,29 +1825,55 @@ public: \end{itemize*} \end{itemize*} +\begin{lstlisting}[ + language=C++, + showspaces=false, + basicstyle=\ttfamily + ] +class MagicString { // Remember me? + std::string* s; +public: + MagicString() : s(new std::string("wiz!")) {} + MagicString(const MagicString\& m) : s(new std::string(*m.s)) {} + std::string* magicStr() { return s; } + // Neu: = operator erlaubt Zuweisungen + MagicString\& operator=(const MagicString\& other) { + if(this != \&other) { + // ACHTUNG beide Werte werden dereferenziert... + // ruft operator= in std::string auf $\rightarrow$ String wird kopiert + *s = *other.s; + } + return *this; + } + ~MagicString() { delete s; } +}; +\end{lstlisting} + + \subsubsection{Templates} \begin{itemize*} \item Generische Datentypen werden in C++ mit Templates realsiert \item Häufig ähnlich eingesetzt wie Generics, aber können neben Typen auch Konstanten enthalten - \item Zur Compile-Zeit aufgelöst $rightarrow$ Deklaration \& Implementierung in Header-Dateien + \item Zur Compile-Zeit aufgelöst $\rightarrow$ Deklaration \& Implementierung in Header-Dateien \item Einfaches Beispiel (mit Typen, ähnl. zu Generics, primitive Typen ok!): \end{itemize*} + \begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] - template // typename keyword -> deklariert T als Typ - T max(T a, T b) { - return (a > b ? a : b); - } + language=C++, + showspaces=false, + basicstyle=\ttfamily +] +template // typename keyword -> deklariert T als Typ +T max(T a, T b) { +return (a > b ? a : b); +} \end{lstlisting} \begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] + language=C++, + showspaces=false, + basicstyle=\ttfamily +] int i = 10; int j = 2; int k = max(j, i); // explizit @@ -2165,63 +1887,60 @@ int l = max(j, i); // automat. Typinferenz durch Parametertypen \item Wir können alternativ versuchen, durch SFINAE zu verhindern, dass Funktionen doppelt definiert sind \item Trick: Einführen eines Pseudoparameters, der nicht benutzt wird \begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] - template - T quadrieren(T i, typename T::Val pseudoParam = 0) { - T b(i); b *= i; return b; - } - \end{lstlisting} + language=C++, + showspaces=false, + basicstyle=\ttfamily +] +template +T quadrieren(T i, typename T::Val pseudoParam = 0) { +T b(i); b *= i; return b; +} +\end{lstlisting} \item Trick: Einführen eines Hilfstemplates (sogenannter trait): wenn arithmetic::Cond definiert ist, muss T = int sein \begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] - template struct arithmetic {}; - template<> struct arithmetic { using Cond = void*; }; - \end{lstlisting} + language=C++, + showspaces=false, + basicstyle=\ttfamily +] +template struct arithmetic {}; +template<> struct arithmetic { using Cond = void*; }; +\end{lstlisting} \item Definition einer Funktion, die nur für int instanziiert werden kann: \begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] - template - T quadrieren(T i, typename arithmetic::Cond = nullptr) { - return i * i; - } - \end{lstlisting} + language=C++, + showspaces=false, + basicstyle=\ttfamily +] +template +T quadrieren(T i, typename arithmetic::Cond = nullptr) { +return i * i; +} +\end{lstlisting} \end{itemize*} \end{itemize*} - \subsubsection{Container} + \begin{itemize*} \item Templates werden an vielen Stellen der C++ Standard-Bibliothek verwendet \item Container implementieren alle gängigen Datenstrukturen \item Prominente Beispiele: \begin{lstlisting}[ - language=C++, - showspaces=false, - basicstyle=\ttfamily - ] + language=C++, + showspaces=false, + basicstyle=\ttfamily + ] template class vector; // dynamisches Array template class list; // doppelt verkette Liste template class set; // geordnete Menge basiert auf Baum -template class map; // Assoziatives Array, geordnet -// wie oben aber basierend auf Hash-Datenstruktur -template class unordered_set; -template class unordered_map; +template class map; // Assoziatives Array, \end{lstlisting} - \item Alle Templates sind stark vereinfacht dargestellt, weitere Parameter haben Standardwerte, die z.B. Speicherverhalten regeln \end{itemize*} + \paragraph{Container Enumerieren} \begin{itemize*} \item Je nach Struktur unterschiedlicher Zugriff @@ -2275,8 +1994,8 @@ template class unordered_map; \item std::set, std::map, ... löschen nur mit erase() \end{itemize*} - \subsubsection{Shared Pointer} + \begin{itemize*} \item Synonym: Smart Pointer \item Ziel: Sichereres Verwenden von Speicher @@ -2321,6 +2040,137 @@ public: }; // TODO operator= implementieren! \end{lstlisting} +\begin{lstlisting}[ + language=C++, + showspaces=false, + basicstyle=\ttfamily + ] +template class shared_ptr { // Vereinfacht! + T* p; // Zeiger auf eigentliches Objekt + int*r; // Referenzzähler +public: + // neue Referenz auf Objekt erzeugen + shared_ptr(T* t) : p(t), r(new int) { *r = 1; } + // Referenz durch andere Referenz erzeugen + shared_ptr(const shared_ptr\& sp) : p(sp.p), r(sp.r) { ++(*r); } + T* operator$\rightarrow$() const { // benutzen wie einen richtigen Zeiger + return p; + } + ~shared_ptr() { + if(--(*r) == 0) { // Objekt loeschen, wenn letzte Referenz weg + delete r; + delete p; +}}}; +\end{lstlisting} + + +\subsubsection{Vergleich mit Java} +\begin{itemize*} + \item Unterschiede im Aufbau: + \begin{itemize*} + \item C++ hat globale Funktionen, also außerhalb von Klassen, wie main + \item \#include gibt Dateien mit Klassen- und Funktionsdefinitionen an, die der Compiler einlesen soll + \item Java-Programme werden in packages gegliedert, in C++ gibt es mit modules ein ähnliches Konzept, welches aber (noch) nicht verbreitet ist + \item C++-Programme können (ohne Bezug zu Dateien) in namespaces untergliedert werden, hier std + \end{itemize*} + \item Programmargumente: + \begin{itemize*} + \item In Java bekommt main ein String-Array übergeben, die Länge kann über .length abgefragt werden + \item C/C++-Programme erhalten ein Array von char* (Details zu Pointern folgen) + \item In C/C++ sind Arrays keine Pseudoobjekte, sondern Speicherbereiche in denen die Daten konsekutiv abgelegt sind $\rightarrow$ argc wird benötigt die Anzahl an Elementen zu kodieren + \end{itemize*} + \item Rückgabewerte: + \begin{itemize*} + \item In Java keine Rückgabe in der main-Methode + \item In C++ Rückgabe eines exit code + \begin{itemize*} + \item 0 gibt an: Programmausführung erfolgreich + \item Andere Werte geben einen Programm-spezifischen Fehlercode zurück + \end{itemize*} + \end{itemize*} + \item Primitive Datentypen: + \begin{itemize*} + \item Wie in Java einfache Datentypen, die "Zahlen" enthalten + \item char, short, int, long sind auf 64-bit Maschinen 8 bit, 16 bit, 32 bit und 64 bit breit (char braucht in Java 16 Bit!) + \item long ist auf 32 bit Maschinen 32 Bit breit, long long [sic!] ist immer 64 Bit + \item bool speichert Boolsche Werte (Breite hängt vom Compiler ab!) + \item Ein unsigned vor Ganzahltypen gibt an, dass keine negativen Zahlen in der Variable gespeichert werden (Beispiel: unsigned int) $\rightarrow$ Kann größere Zahlen speichern \& zu viel Unsinn führen (beim Vergleich mit vorzeichenbehafteten Zahlen) + \end{itemize*} +\end{itemize*} + +\begin{lstlisting}[ + language=java, + showspaces=false, + basicstyle=\ttfamily + ] +[Hello.java] +package hello; // say that we are part of a package + public class Hello { // declare a class called Hello: + // declare the function main that takes an array of Strings: + public static void main(String args[]) { + // call the static method, println on class System.out with parameter "Hi Welt!": + System.out.println("Hi Welt!"); + } +} // end of class Hello +\end{lstlisting} + +\begin{lstlisting}[ + language=C++, + showspaces=false, + basicstyle=\ttfamily + ] +[Hello.cpp] +// include declarations for I/O library where cout object is specified in namespace std:: +#include +// declare the function main that takes an int and array of strings and returns an int as the exit code +int main(int argc, char* argv[]) { + // stream string to cout object flush line with endl + std::cout << "Hello world!" << std::endl; + return 0; +} // end of main() +\end{lstlisting} + +\subsubsection{Zusammenfassung} +\begin{itemize*} + \item C++ erlaubt sehr detaillierte Kontrolle über Speicher- und Laufzeitverhalten + \item Es ist relativ einfach, schwierig zu findende Fehler einzubauen + \item Die Sprache ist durch Operator-Overloading, Mehrfachvererbung und Templates sehr mächtig + \item Erlaubt hohen Grad an Wiederverwendung + \item Anzahl an Code-Zeilen kann reduziert werden + \item Code kann völlig unlesbar werden! Viele Features sollten nur eingesetzt werden wenn sich dadurch ein wirklicher Vorteil ergibt! +\end{itemize*} + + +\subsection{Objektorientierung am Beispiel Java} +\begin{itemize*} + \item Bekannt: + \begin{itemize*} + \item Grundlegendes Verständnis von Java + \item Kapselung durch Klassen und Vererbung + \end{itemize*} + \item Ziele: + \begin{itemize*} + \item Verständnis der Probleme bei Vererbung und Typersetzbarkeit in objektorientierten Programmiersprachen + \item Kennenlernen der Grundideen generischer und abstrahierender Konzepte in Objekt-orientierter Programmierung (OOP) + \item Praktische Erfahrungen anhand von Java \& C++ + \end{itemize*} + \item Ausdrucksstärke erhöhen, Komplexität verbergen +\end{itemize*} + +\paragraph{Unit-Testing in Java} +\begin{itemize*} + \item De facto Standard: JUnit Framework + \item "Best Practice" für einfachen Einsatz: + \begin{itemize*} + \item Java Code in ein oder mehrere Klassen im Ordner src speichern + \item Im Ordner tests jeweils eine Klasse anlegen, die Funktionen einer Implementierungsklasse prüft + \item Konvention: Testklasse einer Klasse Name heißt NameTest + \item Eigentliche Tests werden in Methoden implementiert, die als Tests annotiert sind + \item Typischer Ansatz: für bekannte Werte ausführen und Ergebnis mit Grundwahrheit (erwartetes Verhalten) vergleichen, bspw. mit assertEquals-Funktion + \end{itemize*} + \item Viele weitere Features, z.B. Deaktivieren von Tests, Timeouts, GUI Coverage, Mocks +\end{itemize*} + \section{Einführung in Funktionale Programmierung} Sind $v_1, ..., v_n$ Unbestimmte vom Typ $T_1, ..., T_n$ (bool oder int) und ist $t(v_1,...,v_n)$ ein Term, so heißt $f(v_1,...,v_n)= t(v_1,...,v_n)$ eine Funktionsdefinition vom Typ $T$. $T$ ist dabei der Typ des Terms. @@ -2328,37 +2178,37 @@ Sind $v_1, ..., v_n$ Unbestimmte vom Typ $T_1, ..., T_n$ (bool oder int) und ist Ein **applikativer Algorithmus** ist eine Menge von Funktionsdefinitionen $f_1(v_{1,1}, ..., v_{1,n_1}) = t_1(v_{1,1},...,v_{1,n_1},..., f_m(v_{m,1},...,v_{m,n_m}) = t_m(v_{m,1},..., v_{m,n_m}))$. Die erste Funktion $f_1$ wird wie beschrieben ausgewertet und ist die Bedeutung (Semantik) des Algorithmus. Kategorien der funktionalen Sprachen -\begin{itemize} +\begin{itemize*} \item Ordnung der Sprache - \begin{itemize} - \item Erster Ordnung: - \begin{itemize} - \item Funktionen können (nur) definiert und aufgerufen werden - \end{itemize} - \item Höherer Ordnung: - \begin{itemize} - \item Funktionen können außerdem als Parameter an Funktionen übergeben werden und/oder Ergebnisse von Funktionen sein. - \item Funktionen sind hier auch Werte! -- erstklassige Werte; - \item Erstklassig: Es gibt keine Einschränkungen. - \item Umgekehrt: Wert ist eine Funktion ohne Parameter - \end{itemize} - \end{itemize} + \begin{itemize*} + \item Erster Ordnung: + \begin{itemize*} + \item Funktionen können (nur) definiert und aufgerufen werden + \end{itemize*} + \item Höherer Ordnung: + \begin{itemize*} + \item Funktionen können außerdem als Parameter an Funktionen übergeben werden und/oder Ergebnisse von Funktionen sein. + \item Funktionen sind hier auch Werte! -- erstklassige Werte; + \item Erstklassig: Es gibt keine Einschränkungen. + \item Umgekehrt: Wert ist eine Funktion ohne Parameter + \end{itemize*} + \end{itemize*} \item Auswertungsstrategie: - \begin{itemize} - \item Strikte Auswertung: - \begin{itemize} - \item Synonyme: strict evaluation, eager evaluation, call by value, applikative Reduktion - \item Die Argumente einer Funktion werden vor Eintritt in die Funktion berechnet (ausgewertet) - wie z.B. in Pascal oder C. - \end{itemize} - \item Bedarfsauswertung: - \begin{itemize} - \item Synonyme: Lazy evaluation, call by need - \item Funktionsargumente werden unausgewertet an die Funktion übergeben - \item Erst wenn die Funktion (in ihrem Körper) die Argumente benötigt, werden die eingesetzten Argumentausdrücke berechnet, und dann nur einmal. - \item Realisiert "Sharing" (im Unterschied zur Normalform-Reduktion - dort werden gleiche Ausdrücke immer wieder erneut berechnet). - \end{itemize} - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Strikte Auswertung: + \begin{itemize*} + \item Synonyme: strict evaluation, eager evaluation, call by value, applikative Reduktion + \item Die Argumente einer Funktion werden vor Eintritt in die Funktion berechnet (ausgewertet) - wie z.B. in Pascal oder C. + \end{itemize*} + \item Bedarfsauswertung: + \begin{itemize*} + \item Synonyme: Lazy evaluation, call by need + \item Funktionsargumente werden unausgewertet an die Funktion übergeben + \item Erst wenn die Funktion (in ihrem Körper) die Argumente benötigt, werden die eingesetzten Argumentausdrücke berechnet, und dann nur einmal. + \item Realisiert "Sharing" (im Unterschied zur Normalform-Reduktion - dort werden gleiche Ausdrücke immer wieder erneut berechnet). + \end{itemize*} + \end{itemize*} +\end{itemize*} \subsection{Applikaive Algorithmen} Grundidee: @@ -2647,7 +2497,7 @@ Was passiert wenn wir mathstuff:factorial() mit einem negativen Argument aufrufe \item Erste Reaktion: rette das Laufzeitsystem durch Eingabe von CTRL-G \begin{itemize*} \item User switch command - \begin{enumerate} + \begin{enumerate*} \item --> h \item c [nn] - connect to job \item i [nn] - interrupt job @@ -2658,7 +2508,7 @@ Was passiert wenn wir mathstuff:factorial() mit einem negativen Argument aufrufe \item q - quit erlang \item ? | h - this message \item --> - \end{enumerate} + \end{enumerate*} \item Liste durch Eingabe von j alle Jobnummern auf \item Beende den entsprechenden Shell-Job durch k \item Starte eine neue Shell durch Eingabe von s @@ -2784,11 +2634,11 @@ Anonyme Funktionen: \item Die Funktionen, die anonyme Funktionen als Parameter akzeptieren bzw. als Ergebnis zurückgeben nennt man Funktionen höherer Ordnung \end{itemize*} -\begin{itemize} +\begin{itemize*} \item Erlang-Programme werden durch Definition der entsprechenden Funktionen in Modulen erstellt \item Module können in den Erlang-Interpreter geladen und von diesem in Zwischencode übersetzt werden \item Anschließend können Anfragen im Interpreter gestellt werden -\end{itemize} +\end{itemize*} Modul fakultaet.erl: \begin{lstlisting}[ @@ -2819,7 +2669,7 @@ fakultaet:fak(5). \end{lstlisting} \paragraph{Elemente von Erlang} -\begin{itemize} +\begin{itemize*} \item Kommentare: werden mit % eingeleitet und erstrecken sich bis Zeilenende \item Ganzzahlen (Integer): 10, -234, \$A, \$Char, 16\#AB10F \item Gleitkommazahlen (Floats): 17.368, -56.34, 12.34E-10 @@ -2829,20 +2679,20 @@ fakultaet:fak(5). \item Variablen: Abc, A\_long\_variable \#fangen mit Großbuchstaben an; zur Speicherung von Werten; können nur einmal gebunden werden \item Komplexe Datenstrukturen: beliebige komplexe Datenstrukturen können erzeugt werden (durch einfaches Hinschreiben) \item Pattern Matching - \begin{itemize} - \item {B, C, D} = {10, foo, bar} \#bindet B zu 10, C zu foo und D zu bar - \item {A, A, B} = {abc, def, ghi} \#schlägt fehl A=!A - \item [A, B, C, D] = [1, 2, 3] \#schlägt fehl; Länge des ersten Patterns zu groß - \item [A, B | C] = [1, 2, 3, 4, 5] \#bindet A zu 1, B zu 2, C zu [3, 4, 5] - \item [A, \_, B ] = [1, 2, 3] \#bindet A zu 1, B zu 3; 2 anonyme Variable $\rightarrow$ kann nie ausgelesen werden - \end{itemize} + \begin{itemize*} + \item {B, C, D} = {10, foo, bar} \#bindet B zu 10, C zu foo und D zu bar + \item {A, A, B} = {abc, def, ghi} \#schlägt fehl A=!A + \item [A, B, C, D] = [1, 2, 3] \#schlägt fehl; Länge des ersten Patterns zu groß + \item [A, B | C] = [1, 2, 3, 4, 5] \#bindet A zu 1, B zu 2, C zu [3, 4, 5] + \item [A, \_, B ] = [1, 2, 3] \#bindet A zu 1, B zu 3; 2 anonyme Variable $\rightarrow$ kann nie ausgelesen werden + \end{itemize*} \item Funktionsaufrufe - \begin{itemize} - \item module:func(Arg\_1, Arg\_2,...) - \item func(Arg\_1, Arg\_2,...) - \end{itemize} + \begin{itemize*} + \item module:func(Arg\_1, Arg\_2,...) + \item func(Arg\_1, Arg\_2,...) + \end{itemize*} \item Modul Deklaration - \begin{lstlisting}[ + \begin{lstlisting}[ language=erlang, showspaces=false, basicstyle=\ttfamily @@ -2853,48 +2703,48 @@ fakultaet:fak(5). times(X, N) $\rightarrow$ X * N. \end{lstlisting} \item Eingebaute Funktionen (Built In Functions, BIFs) - \begin{itemize} - \item date() - \item time() - \item length([1,2,3,4,5]) - \item size({a,b,c}) - \item atom\_to\_list(an\_atom) - \item list\_to\_tuple([1,2,3,4]) - \item integer\_to\_list(2234) - \item tuple\_to\_list({}) - \end{itemize} + \begin{itemize*} + \item date() + \item time() + \item length([1,2,3,4,5]) + \item size({a,b,c}) + \item atom\_to\_list(an\_atom) + \item list\_to\_tuple([1,2,3,4]) + \item integer\_to\_list(2234) + \item tuple\_to\_list({}) + \end{itemize*} \item Definition von Funktionen: func(Pattern1, Pattern2,...) $\rightarrow$ ...; ... . \#";" kündigt weitere Alternativen an. Am Ende muss "." stehen \item Guards (Wächter): - \begin{lstlisting}[ + \begin{lstlisting}[ language=erlang, showspaces=false, basicstyle=\ttfamily ] factorial(N) when N > 0 $\rightarrow$ N* factorial(N-1); \end{lstlisting} - \begin{itemize} - \item number(X) % X is a number - \item integer(X) % X is an integer - \item float(X) % X is a float - \item atom(X) % X is an atom - \item tuple(X) % X is a tuple - \item list(X) % X is a list - \item length(X) == 3 % X is a list of length 3 - \item size(X) == 2 % X is a tuple of size 2. - \item X > Y + Z % X is > Y + Z - \item X == Y % X is equal to Y - \item X =:= Y % X is exactly equal to Y - \end{itemize} + \begin{itemize*} + \item number(X) % X is a number + \item integer(X) % X is an integer + \item float(X) % X is a float + \item atom(X) % X is an atom + \item tuple(X) % X is a tuple + \item list(X) % X is a list + \item length(X) == 3 % X is a list of length 3 + \item size(X) == 2 % X is a tuple of size 2. + \item X > Y + Z % X is > Y + Z + \item X == Y % X is equal to Y + \item X =:= Y % X is exactly equal to Y + \end{itemize*} \item Traversieren ("Ablaufen") von Liste: - \begin{itemize} - \item sum([H|T]) $\rightarrow$ H + sum(T); sum([]) $\rightarrow$ 0. - \item len([\_|T]) $\rightarrow$ 1 + len(T); len([]) $\rightarrow$ 0. - \end{itemize} + \begin{itemize*} + \item sum([H|T]) $\rightarrow$ H + sum(T); sum([]) $\rightarrow$ 0. + \item len([\_|T]) $\rightarrow$ 1 + len(T); len([]) $\rightarrow$ 0. + \end{itemize*} \item Case-Ausdrücke: - \begin{lstlisting} + \begin{lstlisting} case Expression of Pattern1 [when Guard1] $\rightarrow$ Expr_seq1; ... end \end{lstlisting} -\end{itemize} +\end{itemize*} \subsection{Lambda Kalkül} @@ -2903,11 +2753,11 @@ Hier: $\lambda$-Kalkül (Church, Landin) für sequentielle (funktionale /imperat Definition der $\lambda$-Terme: Die Klasse $\bigwedge$ der Lambda-Terme ist die kleinste Klasse, welche die folgenden Eigenschaften erfüllt: -\begin{itemize} +\begin{itemize*} \item Wenn x eine Variable ist, dann ist $x \in \bigwedge$ \item Wenn $M \in \bigwedge$ ist, dann ist $(\lambda xM) \in \bigwedge$ (Abstraktion) \item Wenn $M, N \in \bigwedge$ sind, dann ist $(MN) \in \bigwedge$ (Funktionsanwendung) -\end{itemize} +\end{itemize*} %| Bezeichnung | Notation | Beispiele | %| -\item | -\item | -\item | @@ -2918,17 +2768,17 @@ Die Klasse $\bigwedge$ der Lambda-Terme ist die kleinste Klasse, welche die folg $\lambda$-Abstraktionen: $\lambda x. t$ bindet die Variable x im Ausdruck t Die Menge der freien Variablen eines Terms M wird mit FV(M) bezeichnet und ist wie folgt induktiv definiert: -\begin{itemize} +\begin{itemize*} \item $FV(x)={x}$ \item $FV(MN) = FV(M) \cup FV(N)$ \item $FV(\lambda x.M) = FV(M) - {x}$ -\end{itemize} +\end{itemize*} Ein Lambda-Term ohne freie Variablen heißt Kombinator -\begin{itemize} +\begin{itemize*} \item Identitätsfunktion: $I \equiv \lambda x.x$ \item Konstanten-Funktional: $K \equiv \lambda xy.x$ \item Fixpunkt-Kombinator: $Y \equiv \lambda f.(\lambda x. f (x x)) (\lambda x. f (x x))$ -\end{itemize} +\end{itemize*} \subsection{Äquivalenz} $\alpha$-Äquivalenz: $t_1$ und $t_2$ heißen $\alpha$-äquivalent $(t_1 t_2)$, wenn $t_1$ in $t_2$ durch konsistente Umbenennung der $\lambda$-gebundenen Variablen überführt werden kann. @@ -2936,22 +2786,22 @@ $\alpha$-Äquivalenz: $t_1$ und $t_2$ heißen $\alpha$-äquivalent $(t_1 t_2)$, $\eta $-Äquivalenz: Terme $\lambda x. f\ x$ und $f$ heißen $\eta $-äquivalent $(\lambda x. f x = f )$ falls $x$ nicht freie Variable von f ist \subsection{Ausführung von $\lambda$ Termen} -\begin{itemize} +\begin{itemize*} \item Redex: Ein $\lambda $-Term der Form $(\lambda x. t_1 ) t_2$ heißt Redex. \item $\beta$ -Reduktion: $\beta$ -Reduktion entspricht der Ausführung der Funktionsanwendung auf einem Redex: $(\lambda x. t_1 ) t_2 \Rightarrow t_1 [x \rightarrow t_2 ]$ \item Substitution: $t_1 [x \rightarrow t_2 ]$ erhält man aus dem Term $t_1$ , wenn man alle freien Vorkommen von $x$ durch $t_2$ ersetzt. \item Normalform: Ein Term, der nicht weiter reduziert werden kann, heißt in Normalform. -\end{itemize} +\end{itemize*} Beispiel: $(\lambda x.x)y \Rightarrow x[x \rightarrow y] = y$ \subsection{Kodierung boolscher Werte} Church Booleans -\begin{itemize} +\begin{itemize*} \item True wird zu: $C_{true} = \lambda t.\lambda f.t$ \item False wird zu: $C_{false} = \lambda t.\lambda f.f$ \item If-then-else wird zu: $If = \lambda a.a$ -\end{itemize} +\end{itemize*} Bsp: \begin{lstlisting} @@ -2975,56 +2825,56 @@ Nullvergleich: $isZero = \lambda n. n (\lambda x. C false ) C true$ \subsection{Einführung} \subsubsection{Kalküle} Kalküle sind -\begin{itemize} +\begin{itemize*} \item Minimalistische Programmiersprachen zur Beschreibung von Berechnungen, \item mathematische Objekte, über die Beweise geführt werden können. -\end{itemize} +\end{itemize*} In dieser Vorlesung: -\begin{itemize} +\begin{itemize*} \item $\lambda$-Kalkül (Church, Landin) für sequentielle (funktionale/imperative Sprachen) -\end{itemize} +\end{itemize*} Beispiele weiterer Kalküle: -\begin{itemize} +\begin{itemize*} \item CSP (Hoare) Communicating Sequential Processes - für nebenläufige Programme mit Nachrichtenaustausch \item $\pi$-Kalkül (Milner) für nebenläufige, mobile Programme -\end{itemize} +\end{itemize*} \subsubsection{Das untypisierte Lambdakalkül} -\begin{itemize} +\begin{itemize*} \item Turing-mächtiges Modell funktionaler Programme \item Auch: Beschreibung sequentieller imperativer Konstrukte -\end{itemize} +\end{itemize*} \subitem\colorbox{lightgray}{ \begin{minipage}[h]{0.9\linewidth} Definition der $\lambda$-Terme: Die Klasse $\Lambda$ der Lambda-Terme ist die kleinste Klasse, welche die folgenden Eigenschaften erfüllt: - \begin{itemize} + \begin{itemize*} \item Wenn x eine Variable ist, dann ist x $\in \Lambda$ \item Wenn M $\in \Lambda$ ist, dann ist ($\lambda$xM) $\in \Lambda$ (Abstraktion) \item Wenn M, N $\in \Lambda$ sind, dann ist (MN) $\in \Lambda$ (Funktionsanwendung) - - \end{itemize} + + \end{itemize*} \end{minipage} } -\begin{itemize} +\begin{itemize*} \item Um Klammern zu sparen verwendet man oft eine alternative Notation: $\lambda$x.M \item Bei mehreren zu bindenden Variablen: $\lambda$xyz.M = ($\lambda$x($\lambda$y($\lambda$zM))) -\end{itemize} +\end{itemize*} \subsubsection{Strukturelle Induktion} -\begin{itemize} +\begin{itemize*} \item Aufgrund des "rekursiven" Aufbaus der Definition der Klasse $\Lambda$ der Lamda-Terme, können Aussagen über Lambda-Terme mittels \color{blue} "struktureller Induktion" \color{black} geführt werden: - \begin{itemize} - \item Hierbei folgt der Induktionsbeweis der Struktur der Lambda-Terme, wie er in der Definition vorgegeben wird - \end{itemize} + \begin{itemize*} + \item Hierbei folgt der Induktionsbeweis der Struktur der Lambda-Terme, wie er in der Definition vorgegeben wird + \end{itemize*} \item Beispiel: Jeder Term in $\Lambda$ ist wohl geklammert - \begin{itemize} - \item \color{blue}{Induktionsanfang:} \color{black} trivial, da jede Variable ein wohlgeklammerter Lambda-Term ist. - \item \color{blue} Induktionsannahme: \color{black} M,N sind wohlgeklammerte Lambda-Terme - \item \color{blue} Induktionsschritt: \color{black} dann sind auch die Terme (MN) und ($\lambda$xM) wolgeklammert. - \end{itemize} -\end{itemize} + \begin{itemize*} + \item \color{blue}{Induktionsanfang:} \color{black} trivial, da jede Variable ein wohlgeklammerter Lambda-Term ist. + \item \color{blue} Induktionsannahme: \color{black} M,N sind wohlgeklammerte Lambda-Terme + \item \color{blue} Induktionsschritt: \color{black} dann sind auch die Terme (MN) und ($\lambda$xM) wolgeklammert. + \end{itemize*} +\end{itemize*} \subsubsection{Das untypisierte $\lambda$-Kalkül} @@ -3070,10 +2920,10 @@ Variablenbindung in Haskell (erlaubt anonyme Lambda-Funktionen): \\ Analog bei $\lambda$-Abstraktionen: $\lambda$x.t bindet die Variable x im Ausdruck t\\ Beispiele: -\begin{itemize} +\begin{itemize*} \item $\lambda$x.$\lambda$y.fyx bindet x in $\lambda$y.fyx, das selbst x in fyx bindet. \item f ist frei in $\lambda$x.$\lambda$y.fyx. -\end{itemize} +\end{itemize*} \ \\ Innere Abstraktionen können äußere Variablen verdecken: \ \\ \begin{center} @@ -3084,30 +2934,30 @@ Innere Abstraktionen können äußere Variablen verdecken: \ \\ \end{center} \subsubsection{Freie und gebundene Variablen} -\begin{itemize} +\begin{itemize*} \item Die Menge der \color{blue} freien Variablen \color{black} eines Terms M wird mit FV(M) bezeichnet und ist wie folgt induktiv definiert: - \begin{itemize} - \item FV(x) = \{x\} - \item FV(MN) = FV(M) $\cup $ FV(N) - \item FV($\lambda$x.M) = FV(M) - \{x\} - \end{itemize} + \begin{itemize*} + \item FV(x) = \{x\} + \item FV(MN) = FV(M) $\cup $ FV(N) + \item FV($\lambda$x.M) = FV(M) - \{x\} + \end{itemize*} \item Übung: Definieren sie analog die Menge der gebundenen Variablen GV(M) \item Ein Lambda-Term ohne freie Variablen heißt \color{blue}Kombinator \color{black} \item Einige besonders wichtige Kombinatoren haben eigene Namen: - \begin{itemize} - \item Identitätsfunktion:\enspace\enspace \enspace \enspace\enspace\enspace\enspace I $\equiv$ $\lambda$x.x - \item Konstanten-Funktional: \enspace K $\equiv$ $\lambda$xy.x - \item Fixpunkt-Kombinator: \enspace\enspace Y $\equiv$ $\lambda$f.($\lambda$x.f(x x)) ($\lambda$x.f(x x)) - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Identitätsfunktion:\enspace\enspace \enspace \enspace\enspace\enspace\enspace I $\equiv$ $\lambda$x.x + \item Konstanten-Funktional: \enspace K $\equiv$ $\lambda$xy.x + \item Fixpunkt-Kombinator: \enspace\enspace Y $\equiv$ $\lambda$f.($\lambda$x.f(x x)) ($\lambda$x.f(x x)) + \end{itemize*} +\end{itemize*} \subsection{Rechenregeln} \subsubsection{$\alpha$-Äquivalenz} Namen gebundener Variablen -\begin{itemize} +\begin{itemize*} \item dienen letztlich nur der Dokumentation \item entscheidend sind die Bindungen -\end{itemize} +\end{itemize*} \colorbox{lightgray}{ \begin{minipage}[h]{1.0\linewidth} @@ -3129,9 +2979,9 @@ aber \subsubsection{$\eta$-Äquivalenz} Extensionalitäts-Prinzip: -\begin{itemize} +\begin{itemize*} \item Zwei Funktionen sind gleich, falls Ergebnis gleich für alle Argumente -\end{itemize} +\end{itemize*} \colorbox{lightgray} { \begin{minipage}[h]{1.0\linewidth} $\eta$-Äquivalenz \\ @@ -3141,7 +2991,7 @@ Extensionalitäts-Prinzip: Beispiele: \begin{center} $\lambda$x.$\lambda$y.f z x y $\stackrel{\eta}{=}$ $\lambda$x.f z x \newline \newline - f z $\stackrel{\eta}{=}$ $\lambda$x.f z x \newline \newline $\lambda$x.x $\stackrel{\eta}{=}$ $\lambda$x.($\lambda$x.x)x \newline \newline + f z $\stackrel{\eta}{=}$ $\lambda$x.f z x \newline \newline $\lambda$x.x $\stackrel{\eta}{=}$ $\lambda$x.($\lambda$x.x)x \newline \newline \end{center} aber \begin{center} $\lambda$x.f x x $\stackrel{\eta}{\neq}$ f x @@ -3222,28 +3072,28 @@ Beispiel: let x = g y in f x berechnet f(g y)\\ \begin{center} \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-003.png} - \end{center} +\end{center} -\begin{itemize} +\begin{itemize*} \item if True then x else y ergibt: - \subitem ($\lambda$\color{blue}a.a\color{black})(\color{red} $\lambda$t.$\lambda$f.t\color{black}) x y $\Rightarrow$ ($\lambda$\color{blue}t\color{black}.$\lambda$f.\color{blue}t\color{black}) \color{red}x\color{black} y $\Rightarrow$ ($\lambda$\color{blue}f\color{black}.x)\color{red}y\color{black} $\Rightarrow$ x + \subitem ($\lambda$\color{blue}a.a\color{black})(\color{red} $\lambda$t.$\lambda$f.t\color{black}) x y $\Rightarrow$ ($\lambda$\color{blue}t\color{black}.$\lambda$f.\color{blue}t\color{black}) \color{red}x\color{black} y $\Rightarrow$ ($\lambda$\color{blue}f\color{black}.x)\color{red}y\color{black} $\Rightarrow$ x \item $b_1$ \&\& $b_2$ ist äquivalent zu if $b_1$ then $b_2$ else False - \subitem $\Rightarrow$ $b_1$ \&\& $b_2$ wird zu ($\lambda$a.a) $b_1$ $b_2$ $C_false$ - \subitem $\Rightarrow$ $b_1$ \&\& $b_2$ wird zu ($\lambda$a.a) $b_1$ $b_2$ ($\lambda$t.$\lambda$f.f) + \subitem $\Rightarrow$ $b_1$ \&\& $b_2$ wird zu ($\lambda$a.a) $b_1$ $b_2$ $C_false$ + \subitem $\Rightarrow$ $b_1$ \&\& $b_2$ wird zu ($\lambda$a.a) $b_1$ $b_2$ ($\lambda$t.$\lambda$f.f) \item True \&\& True ergibt: - \subitem \color{white} $\Rightarrow$ \color{black}($\lambda$\color{blue}a.a\color{black})\color{red}$C_true$ \color{black} $C_true$ ($\lambda$t.$\lambda$f.f) - \subitem $\Rightarrow$ ($\lambda$\color{blue}t\color{black}.$\lambda$f.\color{blue}t\color{black})\color{red}($\lambda$t.$\lambda$f.t)\color{black}($\lambda$t.$\lambda$f.f) - \subitem $\Rightarrow$ ($\lambda$\color{blue}f\color{black}.($\lambda$t.$\lambda$f.t)) \color{red}($\lambda$t.$\lambda$f.f)\color{black} $\Rightarrow$ $\lambda$t.$\lambda$f.f = $C_true$ -\end{itemize} + \subitem \color{white} $\Rightarrow$ \color{black}($\lambda$\color{blue}a.a\color{black})\color{red}$C_true$ \color{black} $C_true$ ($\lambda$t.$\lambda$f.f) + \subitem $\Rightarrow$ ($\lambda$\color{blue}t\color{black}.$\lambda$f.\color{blue}t\color{black})\color{red}($\lambda$t.$\lambda$f.t)\color{black}($\lambda$t.$\lambda$f.f) + \subitem $\Rightarrow$ ($\lambda$\color{blue}f\color{black}.($\lambda$t.$\lambda$f.t)) \color{red}($\lambda$t.$\lambda$f.f)\color{black} $\Rightarrow$ $\lambda$t.$\lambda$f.f = $C_true$ +\end{itemize*} -\begin{itemize} +\begin{itemize*} \item $b_1 \lor b_2$ entspricht: - \subitem if $b_1$ then True else $b_2$ + \subitem if $b_1$ then True else $b_2$ \item $\neg b_1$ entspricht: - \subitem if $b_1$ then False else True + \subitem if $b_1$ then False else True \item $b_1 \Rightarrow b_2$ entspricht: - \subitem if $b_1$ then $b_2$ else True -\end{itemize} + \subitem if $b_1$ then $b_2$ else True +\end{itemize*} \subsubsection{Kodierung natürlicher Zahlen} \begin{center} @@ -3318,37 +3168,37 @@ $\lambda x.xx$ wendet sein Argument auf das Argument selbst an $\Rightarrow$ dad \end{minipage} } \newline -\begin{itemize} +\begin{itemize*} \item Der Fixpunktsatz besagt, dass im Lambda-Kalkül jeder Term einen Fixpunkt hat, d.h. einen Wert, der auf sich selber abgebildet wird. \item Beweis: - \begin{itemize} - \item Zu jedem beliebigen F sei W = $\lambda$x.F(x x) und X = (W W) - \item Dann gilt: X $\equiv$ WW $\equiv$ ($\lambda$x.F(x x)) W $\equiv$ F(W W) $\equiv$ F X - \end{itemize} + \begin{itemize*} + \item Zu jedem beliebigen F sei W = $\lambda$x.F(x x) und X = (W W) + \item Dann gilt: X $\equiv$ WW $\equiv$ ($\lambda$x.F(x x)) W $\equiv$ F(W W) $\equiv$ F X + \end{itemize*} \item Bemerkungen: - \begin{itemize} - \item Für einige Lambda-Terme ist die Identifikation eines Fixpunktes einfach, z.B. für den Term $\lambda$x.x (alle Terme sind Fixpunkte) - \item Für andere Terme, wie $\lambda$xy.xy (= $\lambda$x.$\lambda$y.xy) ist das nicht so klar - \item Der Beweis des Fixpunktsatzes ist konstruiv, d.h. er liefert zu jedem Lambda-Term einen Fixpunkt - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Für einige Lambda-Terme ist die Identifikation eines Fixpunktes einfach, z.B. für den Term $\lambda$x.x (alle Terme sind Fixpunkte) + \item Für andere Terme, wie $\lambda$xy.xy (= $\lambda$x.$\lambda$y.xy) ist das nicht so klar + \item Der Beweis des Fixpunktsatzes ist konstruiv, d.h. er liefert zu jedem Lambda-Term einen Fixpunkt + \end{itemize*} +\end{itemize*} \subsubsection{Anwendung des Fixpunktsatzes} -\begin{itemize} +\begin{itemize*} \item Aufgabe: Berechne den Fixpunkt zum Term $\lambda$xy.xy - \begin{itemize} - \item Lösungsansatz: W $\equiv$ $\lambda$x.($\lambda$\color{blue}x\color{black}y.\color{blue}x\color{black}y)\color{red}(xx) \color{black} $\equiv$ $\lambda x.\lambda y.(x x)y \equiv \lambda xy.(xx)y$ - \item Damit ist der gesuchte Fixpunkt X $\equiv ((\lambda xy.(xx)y)(\lambda xy.(xx)y))$ - \item Nachrechnen: - \begin{description} - \item[ \space ] $(\lambda xy.xy) ((\lambda xy.(xx)y) (\lambda xy.(xx)y))$ - \item[$\equiv$] $(\lambda \color{blue}x\color{black}.\lambda y. \color{blue} x \color{black} y) \color{red} ((\lambda xy.(xx)y) (\lambda xy.(xx)y)$ \color{black} - \item[$\equiv$] $\lambda \color{blue}y \color{black}.((\lambda xy.(xx)y)) (\lambda xy.(xx)y) \color{red}y\color{black}$ - \item[$\equiv$] $(\lambda xy.(xx)y)(\lambda xy.(xx)y)$ - \item[$\equiv$] X - \end{description} - \end{itemize} + \begin{itemize*} + \item Lösungsansatz: W $\equiv$ $\lambda$x.($\lambda$\color{blue}x\color{black}y.\color{blue}x\color{black}y)\color{red}(xx) \color{black} $\equiv$ $\lambda x.\lambda y.(x x)y \equiv \lambda xy.(xx)y$ + \item Damit ist der gesuchte Fixpunkt X $\equiv ((\lambda xy.(xx)y)(\lambda xy.(xx)y))$ + \item Nachrechnen: + \begin{description*} + \item[ \space ] $(\lambda xy.xy) ((\lambda xy.(xx)y) (\lambda xy.(xx)y))$ + \item[$\equiv$] $(\lambda \color{blue}x\color{black}.\lambda y. \color{blue} x \color{black} y) \color{red} ((\lambda xy.(xx)y) (\lambda xy.(xx)y)$ \color{black} + \item[$\equiv$] $\lambda \color{blue}y \color{black}.((\lambda xy.(xx)y)) (\lambda xy.(xx)y) \color{red}y\color{black}$ + \item[$\equiv$] $(\lambda xy.(xx)y)(\lambda xy.(xx)y)$ + \item[$\equiv$] X + \end{description*} + \end{itemize*} \item Bemerkung: Der so für die Identitätsfunktion $\lambda x.x$ konstruierte Fixpunkt ist übrigens $(\lambda x.xx)(\lambda x.xx)$, er spielt die besondere Rolle des Standardterms $\bot$ für nicht-terminierende Ausführungen -\end{itemize} +\end{itemize*} \subsubsection{Der Fixpunkt-Kombinator} \colorbox{lightgray}{ @@ -3359,61 +3209,61 @@ $\lambda x.xx$ wendet sein Argument auf das Argument selbst an $\Rightarrow$ dad \end{center} \end{minipage} } -\begin{itemize} +\begin{itemize*} \item Dieser Kombinator spielt eine wichtige Rolle bei der Definition rekursiver Funktionen im Lambda-Kalkül, wie wir im folgenden sehen werden \item Für jeden Lambda-Term M gilt: Y M = M (Y M) - \begin{itemize} - \item Beweisidee: zeige, dass beide Terme auf einen identischen Term reduziert werden können - \end{itemize} + \begin{itemize*} + \item Beweisidee: zeige, dass beide Terme auf einen identischen Term reduziert werden können + \end{itemize*} \item Der Term Y ist übrigens nicht der einzige Kombinator, der Fixpunkte zu Lambda-Termen konstruiert - \begin{itemize} - \item A. Turing: $\Theta \equiv (\lambda xy.y(xxy)) (\lambda xy.y(xxy))$ - \end{itemize} -\end{itemize} + \begin{itemize*} + \item A. Turing: $\Theta \equiv (\lambda xy.y(xxy)) (\lambda xy.y(xxy))$ + \end{itemize*} +\end{itemize*} \subsubsection{Rekursion im Lambda-Kalkül} -\begin{itemize} +\begin{itemize*} \item Die bisher definierten Funktionen waren alle nicht-rekursiv \item Viele Funktionen kann man aber nur unter Zuhilfenahme von Rekursion (bzw. Iteration) beschreiben \item In üblichen Programmiersprachen werden rekursive Funktionsdefinitionen durch die Verwendung von Namen für Funktionen möglich - man verwendet hierbei einfach den Namen der gerade zu definierenden Funktion im Rumpf der Definition: - \begin{itemize} - \item fun fak(i) -$>$ if (i = 0) then 1 else i * fak(i-1). - \end{itemize} + \begin{itemize*} + \item fun fak(i) -$>$ if (i = 0) then 1 else i * fak(i-1). + \end{itemize*} \item Im Lambda-Kalkül gibt es jedoch keine Namen für Funktionen: - \begin{itemize} - \item Daher stellt man eine rekursive Funktion f mittels einer Funktion G dar, die einen zusätzlichen Parameter g hat, an den man dann G selber bildet - \item Schaut kompliziert aus, ist es auch (Q-Q) - \item Warum so kompliziert? Damit die Definition von G im eigenen Rumpf verfügbar ist - - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Daher stellt man eine rekursive Funktion f mittels einer Funktion G dar, die einen zusätzlichen Parameter g hat, an den man dann G selber bildet + \item Schaut kompliziert aus, ist es auch (Q-Q) + \item Warum so kompliziert? Damit die Definition von G im eigenen Rumpf verfügbar ist + + \end{itemize*} +\end{itemize*} \subsubsection{Rekursive Funktionen sind Fixpunkte} -\begin{itemize} +\begin{itemize*} \item Rekursive Funktion von g - \begin{itemize} - \item g = $\lambda$n.…g…n… Rumpf verwendet g - - \end{itemize} + \begin{itemize*} + \item g = $\lambda$n.…g…n… Rumpf verwendet g + + \end{itemize*} \item Daraus gewinnt man das Funktional - \begin{itemize} - \item G = $\lambda$g.$\lambda$n.…g…n… - \end{itemize} + \begin{itemize*} + \item G = $\lambda$g.$\lambda$n.…g…n… + \end{itemize*} \item Falls G einen Fixpunkt g* hat, d.h. G(g*) = g*, so - \begin{itemize} - \item g* = G(g*) = $\lambda$n.…g*…n - \end{itemize} + \begin{itemize*} + \item g* = G(g*) = $\lambda$n.…g*…n + \end{itemize*} \item Vergleiche: g = $\lambda$n.…g…n… -\end{itemize} +\end{itemize*} \begin{center} Rekursive Definition $\Leftrightarrow$ Fixpunkt des Funktionals \end{center} -\begin{itemize} +\begin{itemize*} \item Beispiel: Fakultät - \begin{itemize} - \item g = $\lambda$ n. if isZero n then $c_1$ else (times n g(pred n)) - rekursiv - \item G = $\lambda$g.$\lambda$n.if isZero n then $c_1$ else (times n g(pred n)) - funktional - \end{itemize} -\end{itemize} + \begin{itemize*} + \item g = $\lambda$ n. if isZero n then $c_1$ else (times n g(pred n)) - rekursiv + \item G = $\lambda$g.$\lambda$n.if isZero n then $c_1$ else (times n g(pred n)) - funktional + \end{itemize*} +\end{itemize*} \subsubsection{Der Fixpunktkombinator dient als Rekursionsoperator} Wir berechnen den gesuchten Fixpunkt des Funktionals G mit dem Fixpunktkombinator, der somit als Rekusrionsoperator dient: \\ @@ -3437,129 +3287,129 @@ d.h. \space\space\space Yf ist Fixpunkt von f \subsection{Ausdrucksfähigkeit des Lambdakalküls} \subsubsection{Ausdrucksstärke des Lambdakalküls} -\begin{itemize} +\begin{itemize*} \item Im Folgenden wollen wir zeigen, dass der Lambda-Kalkül genau die rekursiven Funktionen beschreibt \item Eine numerische Funktion ist eine Abbildung f: $\mathbb{N}^k \rightarrow \mathbb{N}$ mit k$ \in \mathbb{N} \cap \{0\}$ \item Wir definieren hierzu: - \begin{itemize} - \item Anfangsfunktionen: - \begin{itemize} - \item Projektion: $U_i^k (n_1,n_2,…,n_k) = n_i$ für $1<=i<=k$ - \item Nullfunktion: Z(n) = 0 - \item Nachfolger: S(n) = n+1 - \end{itemize} - \item Minimalisierung: - \begin{itemize} - \item Für eine Relation P(m) bezeichne $\mu$m[P(m)] die kleinste Zahl m sodass P(m) gilt. - \end{itemize} - \end{itemize} + \begin{itemize*} + \item Anfangsfunktionen: + \begin{itemize*} + \item Projektion: $U_i^k (n_1,n_2,…,n_k) = n_i$ für $1<=i<=k$ + \item Nullfunktion: Z(n) = 0 + \item Nachfolger: S(n) = n+1 + \end{itemize*} + \item Minimalisierung: + \begin{itemize*} + \item Für eine Relation P(m) bezeichne $\mu$m[P(m)] die kleinste Zahl m sodass P(m) gilt. + \end{itemize*} + \end{itemize*} \item Bemerkung: im Folgenden notieren wir $n_1,n_2,…,n_k$ kurz als $\overline{n_k}$ \item Eine numerische Funktion ist Lambda-definierbar, wenn es einen Kombinator M gibt, sodass M $\overline{n_k}$ = f($\overline{n_k}$) -\end{itemize} +\end{itemize*} \subsubsection{Ausdrucksstärke des Lambdakalküls (2)} -\begin{itemize} +\begin{itemize*} \item Im folgenden sei C eine Klasse von numerischen Funktionen, und es gelte $g,h,h_1,h_2,…,h_m \in C$ \item Wir definieren nun die folgenden Eigenschaften: - \begin{itemize} - \item C ist \color{blue} abgeschlossen unter Komposition\color{black}, wenn für jede Funktion f, die über f($\overline{n_k}$) := $g(h_1(\overline{n_k}),…,h_m(\overline{n_k}))$ definiert ist, gilt $f \in C$ - \item C ist \color{blue} abgeschlossen unter primitiver Rekursion\color{black}, wenn für jede Funktion f, die über - \begin{center} - f(0,$\overline{n_k}$) = g($\overline{n_k}$) \\ - f(j+1, $\overline{n_k}$) = h(f(j,$\overline{n_k}$),j,$\overline{n_k}$) - \end{center} - definiert ist, gilt: $f \in C$ - \item C ist \color{blue} abgeschlossen unter unbeschränkter Minimalisierung \color{black}, wenn für jede Funktion f, die über f($\overline{n_k}$) = $\mu$m[g($\overline{n_k}$,m)= 0] definiert ist (wobei für alle $\overline{n_k}$ ein m existiere, sodass g($\overline{n_k}$,m) = 0 ist), gilt $f \in C$ - \end{itemize} -\end{itemize} + \begin{itemize*} + \item C ist \color{blue} abgeschlossen unter Komposition\color{black}, wenn für jede Funktion f, die über f($\overline{n_k}$) := $g(h_1(\overline{n_k}),…,h_m(\overline{n_k}))$ definiert ist, gilt $f \in C$ + \item C ist \color{blue} abgeschlossen unter primitiver Rekursion\color{black}, wenn für jede Funktion f, die über + \begin{center} + f(0,$\overline{n_k}$) = g($\overline{n_k}$) \\ + f(j+1, $\overline{n_k}$) = h(f(j,$\overline{n_k}$),j,$\overline{n_k}$) + \end{center} + definiert ist, gilt: $f \in C$ + \item C ist \color{blue} abgeschlossen unter unbeschränkter Minimalisierung \color{black}, wenn für jede Funktion f, die über f($\overline{n_k}$) = $\mu$m[g($\overline{n_k}$,m)= 0] definiert ist (wobei für alle $\overline{n_k}$ ein m existiere, sodass g($\overline{n_k}$,m) = 0 ist), gilt $f \in C$ + \end{itemize*} +\end{itemize*} \subsubsection{Ausdrucksstärke des Lambda-Kalküls (3)} \colorbox{lightgray}{\begin{minipage}[h]{1.0\linewidth} Definition: \\ Die Klasse der rekursiven Funktionen ist die kleinste Klasse numerischer Funktionen, die alle oben genannten Anfangsfunktionen enthält und abgeschlossen ist unter Komposition, primitiver Rekursion und unbeschränkter Minimalisierung \end{minipage}} \newline -\begin{itemize} +\begin{itemize*} \item \color{blue} Lemma 1: Die Anfangsfunktionen sind Lambda-definierbar \color{black} \item Beweis: - \begin{itemize} - \item $U_i^k$ = $\lambda x_1 x_2 … x_k.x_i$ - \item S = $\lambda n.\lambda s. \lambda z.s(nsz)$ (siehe succ bei Churchzahlen) - \item Z = $\lambda fx.x$ (siehe $c_0$ bei Churchzahlen) - \end{itemize} -\end{itemize} + \begin{itemize*} + \item $U_i^k$ = $\lambda x_1 x_2 … x_k.x_i$ + \item S = $\lambda n.\lambda s. \lambda z.s(nsz)$ (siehe succ bei Churchzahlen) + \item Z = $\lambda fx.x$ (siehe $c_0$ bei Churchzahlen) + \end{itemize*} +\end{itemize*} \subsubsection{Ausdrucksstärke des Lambda-Kalküls (4)} -\begin{itemize} +\begin{itemize*} \item \color{blue} Lemma 2: Die Lambda-definierbaren Funktionen sind abgeschlossen unter primitiver Rekursion \color{black} \item Beweis: Sei f definiert über - \begin{center} - f(0,$\overline{n_k}$) = g($\overline{n_k}$)\\ - f(j+1, $\overline{n_k}$) = h(f(j, $\overline{n_k}$),j,$\overline{n_k}$) - \end{center} - und seien g und h Funktionen (die per Induktionsvoraussetzung) durch die Lambda-terme G und H berechnet werden - \begin{itemize} - \item Intuitiv kann f berechnet werden, indem man überprüft ob j = 0 ist, und wenn ja g($\overline{n_k}$), ansonsten h(f(j, $\overline{n_k}$),j,$\overline{n_k}$) - \item Ein Term M hierfür existiert laut Fixpunktsatz und es gilt: - $M \equiv Y (\lambda f\:x\: \overline{y_k}.if(isZero \: x)(G\:\overline{y_k})(H(f(pred\: x)\overline{y_k})(pred \: x)\overline{y_k}))$ - \end{itemize} -\end{itemize} + \begin{center} + f(0,$\overline{n_k}$) = g($\overline{n_k}$)\\ + f(j+1, $\overline{n_k}$) = h(f(j, $\overline{n_k}$),j,$\overline{n_k}$) + \end{center} + und seien g und h Funktionen (die per Induktionsvoraussetzung) durch die Lambda-terme G und H berechnet werden + \begin{itemize*} + \item Intuitiv kann f berechnet werden, indem man überprüft ob j = 0 ist, und wenn ja g($\overline{n_k}$), ansonsten h(f(j, $\overline{n_k}$),j,$\overline{n_k}$) + \item Ein Term M hierfür existiert laut Fixpunktsatz und es gilt: + $M \equiv Y (\lambda f\:x\: \overline{y_k}.if(isZero \: x)(G\:\overline{y_k})(H(f(pred\: x)\overline{y_k})(pred \: x)\overline{y_k}))$ + \end{itemize*} +\end{itemize*} \subsubsection{Ausdrucksstärke des Lambda-Kalküls (5)} -\begin{itemize} +\begin{itemize*} \item \color{blue} Lemma 3: Die Lambda-definierbaren Funktionen sind abgeschlossen unter unbeschränkter Minimalisierung \color{black} \item Beweis: - \begin{itemize} - \item Sei f über f($\overline{n_k}$) = $\mu$m[g($\overline{n_k}$,m) = 0] definiert, wobei g (per Induktionsvoraussetzung) durch den Lambda-Term G berechnet wird - \item Intuitiv kann man f berechnen, indem man bei 0 beginnend für m überprüft, ob g($\overline{n_k}$,m) = 0 ist, und wenn ja m ausgibt, ansonsten die Überprüfung mit m+1 fortsetzt - \item Ein Term für eine solche Funktion kann laut Fixpunktsatz konstruiert werden und man erhält mit Anwendung des Fixpunktkombinators zunächst: \\ - $N \equiv Y (\lambda f \: \overline{x_k} \: y. if(isZero \: (G \: \overline{x_k} \: y))y(f\:\overline{x_k}\:(succ \: y)))$ - \item Nun definiert man die Funktion f durch den folgenden Term M: \\ - $M \equiv \lambda \overline{x_k}.N \: \overline{x_k} \: c_0$ - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Sei f über f($\overline{n_k}$) = $\mu$m[g($\overline{n_k}$,m) = 0] definiert, wobei g (per Induktionsvoraussetzung) durch den Lambda-Term G berechnet wird + \item Intuitiv kann man f berechnen, indem man bei 0 beginnend für m überprüft, ob g($\overline{n_k}$,m) = 0 ist, und wenn ja m ausgibt, ansonsten die Überprüfung mit m+1 fortsetzt + \item Ein Term für eine solche Funktion kann laut Fixpunktsatz konstruiert werden und man erhält mit Anwendung des Fixpunktkombinators zunächst: \\ + $N \equiv Y (\lambda f \: \overline{x_k} \: y. if(isZero \: (G \: \overline{x_k} \: y))y(f\:\overline{x_k}\:(succ \: y)))$ + \item Nun definiert man die Funktion f durch den folgenden Term M: \\ + $M \equiv \lambda \overline{x_k}.N \: \overline{x_k} \: c_0$ + \end{itemize*} +\end{itemize*} \subsubsection{Ausdrucksstärke des Lambda-Kalküls (6)} \begin{center} \includegraphics[width=1.0\linewidth]{Assets/Programmierparadigmen-004.png} \end{center} -\begin{itemize} +\begin{itemize*} \item Aus den Lemmata 1 bis 3 folgt nun der Satz:\\ - \colorbox{lightgray}{ - \begin{minipage}[h]{1.0\linewidth} - Alle rekursiven Funktionen sind Lambda-definierbar - \end{minipage}} -\end{itemize} + \colorbox{lightgray}{ + \begin{minipage}[h]{1.0\linewidth} + Alle rekursiven Funktionen sind Lambda-definierbar + \end{minipage}} +\end{itemize*} \subsection{Berechnungsreihenfolgen und Konfluenz} \subsubsection{Noch einmal Auswertungsstrategien} -\begin{itemize} +\begin{itemize*} \item Bei unserer initialen Betrachtung der Auswertungsstrategien haben wir die volle $\beta$-Rekursion und die Normalreihenfolge kennengelernt \item Nun wollen wir unsere Betrachtungen hierzu noch einmal vertiefen und definieren zunächst: - \begin{itemize} - \item Ein Redex wird als \color{blue} "äußerst" (outermost) \color{black} bezeichnet, wenn er nicht Teil eines anderen Redex ist. - \item Ein Redex wird als \color{blue} "innerst" (innermost) \color{black} bezeichnet, wenn er keinen eigenständigen Redex beinhaltet - \end{itemize} + \begin{itemize*} + \item Ein Redex wird als \color{blue} "äußerst" (outermost) \color{black} bezeichnet, wenn er nicht Teil eines anderen Redex ist. + \item Ein Redex wird als \color{blue} "innerst" (innermost) \color{black} bezeichnet, wenn er keinen eigenständigen Redex beinhaltet + \end{itemize*} \item Mit diesen Begriffen können im folgenden die gebräuchlichsten Auswertungsstrategien formuliert werden - \begin{itemize} - \item \color{blue} Normal Order: \color{black} Evaluiere Argumente so oft, wie sie verwendet werden - \item \color{blue} Applicative Order: \color{black} Evaluiere Argumente einmal - \item \color{blue} Lazy Evaluation: \color{black} Evaluiere Argumente höchstens einmal - \end{itemize} + \begin{itemize*} + \item \color{blue} Normal Order: \color{black} Evaluiere Argumente so oft, wie sie verwendet werden + \item \color{blue} Applicative Order: \color{black} Evaluiere Argumente einmal + \item \color{blue} Lazy Evaluation: \color{black} Evaluiere Argumente höchstens einmal + \end{itemize*} \item Eine zentrale Kernfrage: \color{blue} Welche Auswertungsstrategie führt (möglichst schnell) zu einem nicht weiter reduzierbaren Term? - \color{black} - \begin{itemize} - \item Bei unserer beispielhaften Berechnung des Terms Fak $c_2$ haben wir nach der initialen Anwendung des Fixpunktkombinators zunächst den Term isZero $c_2$ reduziert. - \item Ebenso hätten wird den weiter innen stehenden Fixpunktkombinator zuerst erneut anwenden können(bei voller $\beta$-Reduktion kann jeder Term jederzeit reduziert werden). - \item Auf diese Weise hätten wir unendlich oft vorgehen, damit einen immer länger werdenden Term ableiten können und somit nicht das gewünschte Resultat $c_2$ berechnet. - \end{itemize} + \color{black} + \begin{itemize*} + \item Bei unserer beispielhaften Berechnung des Terms Fak $c_2$ haben wir nach der initialen Anwendung des Fixpunktkombinators zunächst den Term isZero $c_2$ reduziert. + \item Ebenso hätten wird den weiter innen stehenden Fixpunktkombinator zuerst erneut anwenden können(bei voller $\beta$-Reduktion kann jeder Term jederzeit reduziert werden). + \item Auf diese Weise hätten wir unendlich oft vorgehen, damit einen immer länger werdenden Term ableiten können und somit nicht das gewünschte Resultat $c_2$ berechnet. + \end{itemize*} \item Eine weitere Kernfrage: Angenommen mehrere unterschiedliche Reduktionsreihenfolgen führen zu einem nicht weiter zu reduzierenden Ergebnis - \color{blue} führen all diese Reihenfolgen zum gleichen Ergebnis? \color{black} \item Wir definieren zuerst einen zentralen begriff in diesem Zusammenhang: - \colorbox{lightgray}{\begin{minipage}[h]{1.0\linewidth} - Ein Transitiosnsystem (D, $\rightarrow*$) heißt genau dann konfluent, wenn für alle $t,t_1,t_2 \in D$ gilt: wenn $ t \rightarrow* t_1$ und $t \rightarrow* t_2$, dann gibt es ein $t' \in D$ mit $t_1 \rightarrow* t'$ und $t_2 \rightarrow* t'$ - \end{minipage}} + \colorbox{lightgray}{\begin{minipage}[h]{1.0\linewidth} + Ein Transitiosnsystem (D, $\rightarrow*$) heißt genau dann konfluent, wenn für alle $t,t_1,t_2 \in D$ gilt: wenn $ t \rightarrow* t_1$ und $t \rightarrow* t_2$, dann gibt es ein $t' \in D$ mit $t_1 \rightarrow* t'$ und $t_2 \rightarrow* t'$ + \end{minipage}} \item Wenn der Lambda-Kalkül konfluent ist, kann hieraus gefolgert werden, dass unterschiedliche Reduktionsreihenfolgen, die zu einer nicht mehr weiter zu reduzierenden Form führen, somit auf den gleichen Term führen müssen. \item Achtung: hieraus kann nicht gefolgert werden, dass alle Reduktionsreihenfolgen auf den gleichen Term führen, da dies ja nur für "terminierende" Reduktionsreihenfolgen gilt! -\end{itemize} +\end{itemize*} \subsubsection{Church-Rosser-Eigenschaft} \color{blue} Satz (Church-Rosser) \newline Der untypisierte $\lambda$-Kalkül ist konfluent: Wenn $t \stackrel{*}{\Rightarrow} t_1$ und $t \stackrel{*}{\Rightarrow} t_2$, dann gibt es ein t' mit $t_1 \stackrel{*}{\Rightarrow} t'$ und $t_2 \stackrel{*}{\Rightarrow} t'$ @@ -3571,10 +3421,10 @@ d.h. \space\space\space Yf ist Fixpunkt von f \end{center} \color{black} Beweisidee: Definiere $\stackrel{\rightarrow}{\rightarrow}$ als "parallele" $\beta$-Reduktion. -\begin{itemize} +\begin{itemize*} \item Es gilt: $\Rightarrow \subseteq \stackrel{\rightarrow}{\rightarrow} \subseteq \stackrel{*}{\Rightarrow}$ \item Zeige Diamant Eigenschaft für $\stackrel{\rightarrow}{\rightarrow}$ -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.25\linewidth]{Assets/Programmierparadigmen-011} @@ -3585,212 +3435,212 @@ Die Normalform eines $\lambda$-Terms ist - sofern sie existiert - eindeutig. \co \newline \newline Beweis: -\begin{itemize} +\begin{itemize*} \item $t_1$ und $t_2$ Normalformen von t, d.h. $t \stackrel{*}{\Rightarrow} t_1 \nRightarrow$ und $t \stackrel{*}{\Rightarrow} t_2 \nRightarrow$ \item Nach Chruch-Rosser gibt es t' mit $t_1 \stackrel{*}{\Rightarrow} t'$ und $t_2 \stackrel{*}{\Rightarrow} t'$ \item Nach Annahme $t_1 \nRightarrow$ und $t_2 \nRightarrow$, also $t_1 = t' = t_2$ -\end{itemize}\ \newline +\end{itemize*}\ \newline \color{blue} Bei terminierenden $\beta$-Reduktionen ist irrelevant, welchen Redex man zuerst reduziert!\color{black} \subsection{Auswertung von Parametern in Programmiersprachen} \subsubsection{Behandlung von Parametern in Programmiersprachen} -\begin{itemize} +\begin{itemize*} \item Die Art und Weise, wie in einer Programmiersprache Parametter übergeben - d.h. wie die Reihenfolge und die Zeitpunkte ihrer Auswertung gehandhabt - werden, hat Einfluss auf wichtige Eigenschaften der Sprache: - \begin{itemize} - \item Effizienz der Berechnungen - \item Termininerungsverhalten - \item Ausdruckskraft - \end{itemize} + \begin{itemize*} + \item Effizienz der Berechnungen + \item Termininerungsverhalten + \item Ausdruckskraft + \end{itemize*} \item Hierbei ist es insbesondere von Interesse, wie Parameter gehandhabt werden, deren Werte undefiniert sind (z.B. 1/0)\newline - \colorbox{lightgray}{ - \begin{minipage}[h]{1.0\linewidth} - Wir definieren zunächst den zentralen begriff "strikt": \newline Eine n-stellige Funktion heißt strikt im k-ten Argument ($1<=k<=n$), wenn gilt: f($x_1,x_2,…,x_{k-1},\bot,x_{k+1},…,x_n$) = $\bot$ - \end{minipage}} + \colorbox{lightgray}{ + \begin{minipage}[h]{1.0\linewidth} + Wir definieren zunächst den zentralen begriff "strikt": \newline Eine n-stellige Funktion heißt strikt im k-ten Argument ($1<=k<=n$), wenn gilt: f($x_1,x_2,…,x_{k-1},\bot,x_{k+1},…,x_n$) = $\bot$ + \end{minipage}} \item Ein undefiniertes Argument führt hier zu einem undefinierten Resultat \item Grundsätzlich kann man die Auswertungsstrategien von Programmiersprachen in strikte und nicht-strikte Strategien einteilen; sehr gebräuchlich sind dabei insbesondere: - \begin{itemize} - \item Call by Value: Ausdrücke, die Parameter bei einem Funktionsaufruf beschreiben, werden vor der Übergabe an die Funktion vollständig ausgewertet - \item Call by Name: Ausdrücke, die Parameter bei einem Funktionsaufruf beschreiben, werden nicht bei Übergabe, sondern erst dann ausgewertet, wenn sie in der aufgerufenen Funktion tatsächlich benötigt werden - \end{itemize} + \begin{itemize*} + \item Call by Value: Ausdrücke, die Parameter bei einem Funktionsaufruf beschreiben, werden vor der Übergabe an die Funktion vollständig ausgewertet + \item Call by Name: Ausdrücke, die Parameter bei einem Funktionsaufruf beschreiben, werden nicht bei Übergabe, sondern erst dann ausgewertet, wenn sie in der aufgerufenen Funktion tatsächlich benötigt werden + \end{itemize*} \item Beide Varianten haben spezifische Vor- und Nachteile: - \begin{itemize} - \item Call by Value: weniger Berechnungsaufwand, wenn ein Parameter mehr als einmal im Funktionsrumpf vorkommt; weniger Speicheraufwand bei der Übergabe - \item Call by Name: weniger Berechnungsaufwand, wenn ein Argument nicht zum Ergebnis beiträgt; höherer Aufwand bei Übergabe - \end{itemize} + \begin{itemize*} + \item Call by Value: weniger Berechnungsaufwand, wenn ein Parameter mehr als einmal im Funktionsrumpf vorkommt; weniger Speicheraufwand bei der Übergabe + \item Call by Name: weniger Berechnungsaufwand, wenn ein Argument nicht zum Ergebnis beiträgt; höherer Aufwand bei Übergabe + \end{itemize*} \item Die Programmiersprache Erlang realisiert grundsätzlich eine strikte Handhabung von Parametern, da sie die Strategie Call by Value verwendet \item Allerdings wird bei der Definition einer Funktion der resultierende Wert erst dann berechnet, wenn die Funktion ausgewertet wird - \begin{itemize} - \item Das erlaubt über den Umweg zusätzlicher Funktionsdefinitionen auch die Realisierung einer nicht-strikten Auswertungsstrategie - ermöglicht Nachbildung der sogenannten Lazy-Evaluation - \item hierbei wird ein nicht-strikt zu evaluierendes Argument als Resultat einer anonymen nullstelligen Funktion (ohne Parameter) "verpackt" - \item Im Rumpf der eigentlichen Funktion wird diese Funktion dann ausgewertet (= aufgerufen), wenn feststeht, dass dieses Argument für die Berechnung des Ergebnisses benötigt wird - \item Andere funktionale Sprachen wie Haskell oder Gofer verwenden Call by Name und realisieren damit grundsätzlich Lazy-Evaluation - \end{itemize} - \ \linebreak - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-012} - \end{center} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-013} - \end{center} + \begin{itemize*} + \item Das erlaubt über den Umweg zusätzlicher Funktionsdefinitionen auch die Realisierung einer nicht-strikten Auswertungsstrategie - ermöglicht Nachbildung der sogenannten Lazy-Evaluation + \item hierbei wird ein nicht-strikt zu evaluierendes Argument als Resultat einer anonymen nullstelligen Funktion (ohne Parameter) "verpackt" + \item Im Rumpf der eigentlichen Funktion wird diese Funktion dann ausgewertet (= aufgerufen), wenn feststeht, dass dieses Argument für die Berechnung des Ergebnisses benötigt wird + \item Andere funktionale Sprachen wie Haskell oder Gofer verwenden Call by Name und realisieren damit grundsätzlich Lazy-Evaluation + \end{itemize*} + \ \linebreak + \begin{center} + \centering + \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-012} + \end{center} + \begin{center} + \centering + \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-013} + \end{center} \item Erläuterungen: - \begin{itemize} - \item Im zweiten Beispiel wird der Rückgabewert der übergebenen Funktionne nur ausgewertet, wenn sie im Rumpf der auszuführenden Funktion aufgerufen werden - \item Innerhalb von Erlang-Modulen kann man sich mit Hilfe einer Macro-Definition Schreibarbeit sparen: \newline -define(DELAY(E), fun() -$>$ E end). \newline check() -$>$ test2(true, ?DELAY(3), ?DELAY(4/0)). - \end{itemize} + \begin{itemize*} + \item Im zweiten Beispiel wird der Rückgabewert der übergebenen Funktionne nur ausgewertet, wenn sie im Rumpf der auszuführenden Funktion aufgerufen werden + \item Innerhalb von Erlang-Modulen kann man sich mit Hilfe einer Macro-Definition Schreibarbeit sparen: \newline -define(DELAY(E), fun() -$>$ E end). \newline check() -$>$ test2(true, ?DELAY(3), ?DELAY(4/0)). + \end{itemize*} \item Je nachdem, ob und wie häufig ein übergebener Parameter im Funktionsrumpf benötigt wird, können bei Lazy-Evaluation Berechnungen - \begin{itemize} - \item komplett eingespart oder - \item (in identischer Form) wiederholt erforderlich werden - \item Unter Umständen kann man in der betreffenden Funktion durch Einführung einer temporären Variable redundante Mehrfachberechnungen einsparen ($\rightarrow$ Call by Need) - \end{itemize} + \begin{itemize*} + \item komplett eingespart oder + \item (in identischer Form) wiederholt erforderlich werden + \item Unter Umständen kann man in der betreffenden Funktion durch Einführung einer temporären Variable redundante Mehrfachberechnungen einsparen ($\rightarrow$ Call by Need) + \end{itemize*} \item Die Parameterübergabe ist bei Call by Name in der Regel aufwändiger als bei Call by Value - \begin{itemize} - \item Die meisten Programmiersprachen (Java, C, C++, Pascal etc.) verwenden daher Call by Value ($\rightarrow$ strikte Auswertung) - \item Eine Ausnahme wird oft bei dem IF-Konstrukt gemacht (der auszuführende Code ist hier ja meist auch kein Parameter) - \end{itemize} + \begin{itemize*} + \item Die meisten Programmiersprachen (Java, C, C++, Pascal etc.) verwenden daher Call by Value ($\rightarrow$ strikte Auswertung) + \item Eine Ausnahme wird oft bei dem IF-Konstrukt gemacht (der auszuführende Code ist hier ja meist auch kein Parameter) + \end{itemize*} \item Zu Ausdrucksstärke: während strikte Funktionen durch die Strategie Call by Value realisiert werden, ist es nicht so, dass Lazy Evaluation es erlaubt, alle nicht-strikten Funktionen zu realisieren - \begin{itemize} - \item Die folgenden Gleichungen definieren eine nicht-strikte Multiplikation $\otimes$ auf der Basis der Multiplikation · für Zahlen:\newline \begin{center} - 0 $\otimes$ y = 0 \newline - x $\otimes$ 0 = 0 \newline - x $\otimes$ y = x · y \newline - \end{center} - \item Wenn ein Arguemnt undefiniert ist, dann liefert $\otimes$ ein Ergebnis, sofern das andere Argument zu 0 evaluiert wird ($\rightarrow$ fak(-1) $\otimes$ (fak(3)-6)) - \item Implementiert werden kann die Funktion nur durch eine Art von paralleler Auswertung mit Abbruch der anderen Berechnung sobald 0 als Resultat berechnet und zurückgegeben wurde - \end{itemize} + \begin{itemize*} + \item Die folgenden Gleichungen definieren eine nicht-strikte Multiplikation $\otimes$ auf der Basis der Multiplikation · für Zahlen:\newline \begin{center} + 0 $\otimes$ y = 0 \newline + x $\otimes$ 0 = 0 \newline + x $\otimes$ y = x · y \newline + \end{center} + \item Wenn ein Arguemnt undefiniert ist, dann liefert $\otimes$ ein Ergebnis, sofern das andere Argument zu 0 evaluiert wird ($\rightarrow$ fak(-1) $\otimes$ (fak(3)-6)) + \item Implementiert werden kann die Funktion nur durch eine Art von paralleler Auswertung mit Abbruch der anderen Berechnung sobald 0 als Resultat berechnet und zurückgegeben wurde + \end{itemize*} \item Wir betrachten nun die Beziehungen zwischen Parameterbehandlung in Programmiersprachen und Reduktion von Lambda-Termen -\end{itemize} +\end{itemize*} \subsubsection{Auswertungsstrategien \& Programmiersprachen} \color{blue} Werte in Programmiersprachen wie Haskell: \color{black} -\begin{itemize} +\begin{itemize*} \item Primitive Werte: 2, True \item Funktionen: (\textbackslash x $\rightarrow$ x), (\&\&), (x $\rightarrow$(\textbackslash y $\rightarrow$ y+y)x) -\end{itemize} +\end{itemize*} \ \newline \color{blue} Werte im $\lambda$-Kalkül: -\color{black} \begin{itemize} +\color{black} \begin{itemize*} \item Abstraktionen: $c_2 = \lambda s. \lambda z.s\;(s\;z),\;\;\; C_{true} = \lambda t- \lambda f.t,\;\;\; \lambda x.x,\;\;\; \newline \lambda b_1. \lambda b_2.\;b_1\; b_2 (\lambda t.\lambda f.\;f),\;\;\; \lambda x.\;(\lambda y.\;plus\; yy)x$ -\end{itemize} +\end{itemize*} \color{blue} Auswertungsstrategie: \color{black} Keine weitere Reduzierung von Werten \newline Reduziere keine Redexe unter Abstraktionen (umgeben von $\lambda$):\newline $\Rightarrow$ call-by-name, call-by-value \subsubsection{Call-By-Name} Call-By-Name: Reduziere linkesten äußersten Redex -\begin{itemize} +\begin{itemize*} \item Aber nicht falls von einem $\lambda$ umgeben \newline - \subitem $(\lambda y. (\lambda x.y(\lambda z.z)x))\color{red} ((\lambda x.x)(\lambda y.y))$ \color{black} \newline - \subitem $(\lambda x.((\lambda \color{blue}x.x\color{black}) \color{red}(\lambda y.y)\color{black})(\lambda z.z)x)$ - \begin{center} - Intuition: Reduziere Argumente erst, wenn benötigt - \end{center} - -\end{itemize} + \subitem $(\lambda y. (\lambda x.y(\lambda z.z)x))\color{red} ((\lambda x.x)(\lambda y.y))$ \color{black} \newline + \subitem $(\lambda x.((\lambda \color{blue}x.x\color{black}) \color{red}(\lambda y.y)\color{black})(\lambda z.z)x)$ + \begin{center} + Intuition: Reduziere Argumente erst, wenn benötigt + \end{center} + +\end{itemize*} Auswertung in Haskell: \color{blue} Lazy-Evaluation = call-by-name (+sharing) -\color{black} \begin{itemize} +\color{black} \begin{itemize*} \item Standard-Auswertungsstrategie für Funktionen/Konstruktoren \item[] listOf x = x : listOf x \item[] 3: listOf 3 $\nRightarrow$ \item[] \item[] (div 1 0) : (6 : []) \item[] tail ((div 1 0): (6 : [])) $\Rightarrow$ 6 : [] $\nRightarrow$ -\end{itemize} +\end{itemize*} \subsubsection{Call-By-Value} Call-By-Value: Reduziere linkesten Redex -\begin{itemize} +\begin{itemize*} \item der nicht einen $\lambda$ umgibt \item und dessen Argument ein \color{blue} Wert \color{black} ist - \subsubitem $(\lambda y.(\lambda x.y(\lambda z.z)x)((\lambda \color{blue} x.x \color{black})\color{red}(\lambda y.y)\color{black})$ - \subsubitem $\Rightarrow (\lambda \color{blue}y\color{black}(\lambda x. \color{blue}y\color{black}(\lambda z.z)x))\color{red}(\lambda y.y)\color{black}$ - \subsubitem $\Rightarrow (\lambda x.(\lambda y.y(\lambda z.z)x)) \nRightarrow$ + \subsubitem $(\lambda y.(\lambda x.y(\lambda z.z)x)((\lambda \color{blue} x.x \color{black})\color{red}(\lambda y.y)\color{black})$ + \subsubitem $\Rightarrow (\lambda \color{blue}y\color{black}(\lambda x. \color{blue}y\color{black}(\lambda z.z)x))\color{red}(\lambda y.y)\color{black}$ + \subsubitem $\Rightarrow (\lambda x.(\lambda y.y(\lambda z.z)x)) \nRightarrow$ \item[] Intuition: Argumente vor Funktionsaufruf auswerten \item[] Auswertungsstrategie vieler Sprachen: Java, C, Scheme, ML, … \item[] Arithmetik in Haskell: Auswertung by-value \item[] prodOf(x) = y * prodOf x \item[] 3 * prodOf 3 $\Rightarrow$ 3 * (3 * prodOf 3) $\Rightarrow$ … \item[] ((div 1 0) * 6) * 0 $\Rightarrow$ $\bot$ - \item[] ((div 2 2 ) * 6) * 0 $\Rightarrow$ ((1 * 6) * 0) $\Rightarrow$ 6 * 0 $\Rightarrow$ 0 $\nRightarrow$ \end{itemize} + \item[] ((div 2 2 ) * 6) * 0 $\Rightarrow$ ((1 * 6) * 0) $\Rightarrow$ 6 * 0 $\Rightarrow$ 0 $\nRightarrow$ \end{itemize*} \subsubsection{Vergleich der Auswertungsstrategien} \color{blue} Call-by-name vs. Call-by-value \color{black} -\begin{itemize} +\begin{itemize*} \item Werten nicht immer zur Normalform aus $\lambda x.(\lambda y.y)x$ \item Gibt es Normalform, dann darauf $\beta$-reduzierbar (Church-Rosser) \item Call-by-name terminiert öfter \item[] $Y(\lambda y.z) = \lambda f. (\lambda x.f(x\;x))(\lambda x.f(x\;x))\color{red}(\lambda y.z)\color{black}$ \newline - \subitem $ \Rightarrow \lambda x.(\lambda y.z(x\;x))\color{red} (\lambda x.(\lambda y.z)(x\;x))$ \newline - \subitem $\Rightarrow (\lambda y.z)\color{red}((\lambda x.(\lambda y.z)(x\;x)) (\lambda x.(\lambda y.z (x\;x)))\color{black} \stackrel{cbn}{\Rightarrow} z$ - \subitem \newline - \subitem $\stackrel{cbv}{\Rightarrow} (\lambda y.z)((\lambda x.(\lambda y.z)(x\;x))\color{red}(\lambda x.(\lambda y.z (x\;x))\color{black})$ - \subitem $\stackrel{cbv}{\Rightarrow} (\lambda y.z)((\lambda y.z)((\lambda x.(\lambda y.z)(x\;x)) \color{red} (\lambda x.(\lambda y.z(x\;x)) \color{black}))$ -\end{itemize} + \subitem $ \Rightarrow \lambda x.(\lambda y.z(x\;x))\color{red} (\lambda x.(\lambda y.z)(x\;x))$ \newline + \subitem $\Rightarrow (\lambda y.z)\color{red}((\lambda x.(\lambda y.z)(x\;x)) (\lambda x.(\lambda y.z (x\;x)))\color{black} \stackrel{cbn}{\Rightarrow} z$ + \subitem \newline + \subitem $\stackrel{cbv}{\Rightarrow} (\lambda y.z)((\lambda x.(\lambda y.z)(x\;x))\color{red}(\lambda x.(\lambda y.z (x\;x))\color{black})$ + \subitem $\stackrel{cbv}{\Rightarrow} (\lambda y.z)((\lambda y.z)((\lambda x.(\lambda y.z)(x\;x)) \color{red} (\lambda x.(\lambda y.z(x\;x)) \color{black}))$ +\end{itemize*} \color{blue} Standardisierungssatz \newline Wenn t eine Normalform hat, dann findet Normalreihenfolgenauswertung diese. \color{black} \subsubsection{Abschließende Bemerkungen} -\begin{itemize} +\begin{itemize*} \item Der Lambda-Kalkül wurde in den dreißiger Jahren des 20. Jahrhunderts von Alonzo Church erfunden, um damit grundsätzliche Betrachtungen über berechenbare Funktionen anzustellen \item Trotz der Einfachheit der dem Kalkül zugrunde liegenden Regeln, realisiert er ein universelles Berechnungsmodell \item Der Lambda-Kalkül hat die Entwicklung zahlreicher, für die Informatik wichtiger Konzepte beeinflusst - \begin{itemize} - \item Funktionale Programmiersprachen (die minimalen Funktionen von Lisp wurden auf Grundlage des Lambda-Kalküls definiert) - \item Forschund zu Typsystemen für Programmiersprachen - \item Repräsentation von Logik-Termen im Lambda-Kalkül führte zu Theorembeweisen für Logiken höherer Stufen - \end{itemize} + \begin{itemize*} + \item Funktionale Programmiersprachen (die minimalen Funktionen von Lisp wurden auf Grundlage des Lambda-Kalküls definiert) + \item Forschund zu Typsystemen für Programmiersprachen + \item Repräsentation von Logik-Termen im Lambda-Kalkül führte zu Theorembeweisen für Logiken höherer Stufen + \end{itemize*} \item Manche Puristen vertreten gelegentlich die Ansicht, dass funktionale Programmiersprachen nicht viel mehr sind, als "Lambda-Kalkül mit etwas syntaktischem Zucker" -\end{itemize} +\end{itemize*} \subsection{Zusammenfassung} -\begin{itemize} +\begin{itemize*} \item Funktionale Programmierung folgt einem verallgemeinerten Konzept der Funktionsauswertung \item Die Programmiersprache Erlang ist dynamisch typisiert und unterstützt auch Funktionen höherer Ordnung \item Manche Algorithmen lassen sich in Erlang aufgrund der mächtigen Listenkonstrukte und des flexiblen Pattern Matching sehr kompakt formulieren ($\rightarrow$ Potenzmenge, Quicksort) \item Das heißt jedoch nicht, dass sehr kompakter Code auch zu sehr effizientem Laufzeit- und/oder Speicherbedarf führt - teilweise muss der Code relativ geschickt optimiert werden, um einigermaßen effiziente Lösungen zu erhalten ($\rightarrow$ Quicksort) \item Manche Aufgaben, die in imperativen Programmiersprachen sehr effizient und einfach lösbar sind ($\rightarrow$ Teilen einer Liste in gleich große Hälften) sind mittels Listen nur recht umständlich und aufwendig lösbar \item Es gilt in der Praxis also abzuwägen, für welche Aufgaben eine funktionale Sprache eingesetzt werden soll -\end{itemize} +\end{itemize*} \section{Multithreading und parallele Programmierung} \subsection{Grundlagen} \subsubsection{Lernziele} -\begin{itemize} +\begin{itemize*} \item Programmierung paralleler Algorithmen und Verfahren als Paradigma \item Verständnis grundlegender Architekturen und Modelle \item Praktische Erfahrungen mit Erlang, Java und C++ -\end{itemize} +\end{itemize*} \subsubsection{The free launch is over} Taktfrequenz wächst nur noch langsam -\begin{itemize} +\begin{itemize*} \item Physikalische Gründe: Wärmeentwicklung, Energiebedarf, Kriechströme,… -\end{itemize} +\end{itemize*} Auswege -\begin{itemize} +\begin{itemize*} \item Hyperthreading: - \begin{itemize} - \item Abarbeitung mehrerer Threads auf einer CPU (5-15 \% Performancegewinn) - \item Einfache Hardwareunterstützung (einige Register) - \end{itemize} + \begin{itemize*} + \item Abarbeitung mehrerer Threads auf einer CPU (5-15 \% Performancegewinn) + \item Einfache Hardwareunterstützung (einige Register) + \end{itemize*} \item Multicore: - \begin{itemize} - \item Mehrere CPUs auf einem Chip - \item Billiger als echte Mehrprozessorsysteme - \end{itemize} + \begin{itemize*} + \item Mehrere CPUs auf einem Chip + \item Billiger als echte Mehrprozessorsysteme + \end{itemize*} \item Caching: - \begin{itemize} - \item Vergrößerung L1, L2, L3 Cache - \item Speicherzugriff 10-50 $\cdot$ teurer als Cachezugriff - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Vergrößerung L1, L2, L3 Cache + \item Speicherzugriff 10-50 $\cdot$ teurer als Cachezugriff + \end{itemize*} +\end{itemize*} \subsubsection{Konsequenzen und Trends} -\begin{itemize} +\begin{itemize*} \item Applikationen müssen nebenläufig programmiert werden um CPU auszunutzen $\rightarrow$ Many-Core-Systeme \item CPU-Begrenzung von Applikationen \item Effizienz und Performanceoptimierung werden immer wichtiger \item Unterstützung von Nebenläufigkeit/Parallelität durch Programmiersprachen -\end{itemize} +\end{itemize*} \subsubsection{Einordnung} @@ -3808,18 +3658,18 @@ Auswege \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-0016} \end{center} -\begin{itemize} +\begin{itemize*} \item Zugriff über Bus auf gemeinsamen Speicher \item jeder Prozessor mit eigenen Caches -\end{itemize} +\end{itemize*} \subsubsection{Multicore-Systeme} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-0017} \end{center} -\begin{itemize} +\begin{itemize*} \item mehrere Prozessorkerne auf einem Chip \item Kerne typischerweise mit eigenen L1/L2-Caches und gemeinsamen L3-Cache -\end{itemize} +\end{itemize*} \subsubsection{Symmetrisch vs. Nicht-symmetrisch} @@ -3829,29 +3679,29 @@ SMP \newline Symmetric Multi Processing \newline \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-0018} \end{center} -\begin{itemize} +\begin{itemize*} \item Speicherbandbreite begrenzt und von allen Prozessoren gemeinsam genutzt \item Skalierbarkeit begrenzt \item Single Socket Lösung -\end{itemize} +\end{itemize*} NUMA \newline Non-Uniform Memory Access \newline \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-0019} \end{center} -\begin{itemize} +\begin{itemize*} \item jedem Prozessor sind Teile des Speichers zugeordnet \item lokaler Zugriff ist schneller als entfernter \item Typisch für Multi-Socket Systeme -\end{itemize} +\end{itemize*} \subsubsection{CPU vs. GPU} -\begin{itemize} +\begin{itemize*} \item GPU = Graphics Processing Units \item Hochparallele Prozessorarchitekturen (nicht nur) für Grafikrendering -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.95\linewidth]{Assets/Programmierparadigmen-0020} \end{center} @@ -3863,37 +3713,37 @@ Non-Uniform Memory Access \newline \end{center} \subsubsection{Maße zur Leistungsbewertung} -\begin{itemize} +\begin{itemize*} \item Maße für Laufzeitgewinn durch Parallelisierung \item $T_n$ = Laufzeit des Programms mit n Prozessoren/Kernen \item Speedup - \begin{center} - \begin{displaymath} - Speedup = {{T_1} \over {T_n}} - \end{displaymath} - \end{center} + \begin{center} + \begin{displaymath} + Speedup = {{T_1} \over {T_n}} + \end{displaymath} + \end{center} \item Effizienz - \begin{center} - \begin{displaymath} - Effizienz = {Speedup \over n} - \end{displaymath} - \end{center} -\end{itemize} + \begin{center} + \begin{displaymath} + Effizienz = {Speedup \over n} + \end{displaymath} + \end{center} +\end{itemize*} \subsubsection{Amdahlsches Gesetz} -\begin{itemize} +\begin{itemize*} \item[] Berücksichtigung parallelisierbarer und serieller Anteile im Programmablauf \item n Prozessoren \newline - p = paralleler Anteil - \newline s = serieller Anteil \newline - p+s = 1 + p = paralleler Anteil + \newline s = serieller Anteil \newline + p+s = 1 \item Maximaler Speedup - \newline \begin{gather} - Speedup_{max} = {T_1 \over T_n} \\ - = {{s+p}\over{s+{p \over n}}} \\ - = {1 \over {s+{p \over n}}} - \end{gather} -\end{itemize} + \newline \begin{gather} + Speedup_{max} = {T_1 \over T_n} \\ + = {{s+p}\over{s+{p \over n}}} \\ + = {1 \over {s+{p \over n}}} + \end{gather} +\end{itemize*} \begin{center} \includegraphics[width=0.35\linewidth]{Assets/Programmierparadigmen-0022} \end{center} @@ -3904,30 +3754,30 @@ Non-Uniform Memory Access \newline \subsubsection{Prozesse und Threads} Prozess := Programm in Ausführung; Ausführungsumgebung für ein Programm -\begin{itemize} +\begin{itemize*} \item hat eigenen Adressraum \item Prozessor kann immer nur einen Prozess ausführen -\end{itemize} +\end{itemize*} Thread ("Faden") := leichtgewichtige Ausführungsreinheit oder Kontrollfluss (Folge von Anweisungen) innerhalb eines sich in Ausführung befindlichen Programms -\begin{itemize} +\begin{itemize*} \item "leichtgewichtig" im Vergleich zu Betriebssystemprozess \item Threads eines Prozesses teilen sich den Adressraum \item Thread kann von einer CPU oder einem Core ausgeführt werden -\end{itemize} +\end{itemize*} \subsubsection{Shared Memory vs Message Passing} Art der Kommunikation zwischen Prozessen oder Threads \newline Shared Memory -\begin{itemize} +\begin{itemize*} \item Kommunikation (über Variable im) gemeinsamen Speicher \item Prozess kann direkt auf Speicher eines anderen Prozesses zugreifen \item erfordert explizite Synchronisation, z.B. über zeitkritische Abschnitte -\end{itemize} +\end{itemize*} Message Passing -\begin{itemize} +\begin{itemize*} \item Prozesse mit getrennten Adressräumen; Zugriff nur auf eigenen Speicher \item Kommunikation durch explizites Senden/Empfangen von Nachrichten -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-0024} @@ -3940,26 +3790,26 @@ Message Passing \subsubsection{Parallelisierungsarten} Instruktionsparallelität: -\begin{itemize} +\begin{itemize*} \item parallele Ausführung mehrerer Operationen durch eine CPU-Instruktion \item explizit: Vektorinstruktionen, SIMD \item implizit: Pipelining von Instruktionen -\end{itemize} +\end{itemize*} Taskparallelität -\begin{itemize} +\begin{itemize*} \item Ausnutzung inhärenter Parallelität durch simultane Ausführung unabhängiger Aufgaben -\end{itemize} +\end{itemize*} Datenparalelität: -\begin{itemize} +\begin{itemize*} \item Gemeinsame Operation auf homogener Datenmenge \item Zerlegung eines Datensatzes in kleinere Abschnitte -\end{itemize} +\end{itemize*} \subsubsection{Instruktionsparallelität: SIMD} -\begin{itemize} +\begin{itemize*} \item Autovektorisierung durch Compiler \item explizite Instruktionen -\end{itemize} +\end{itemize*} Beispiel: Addition zweier Vektoren \begin{center} @@ -3967,75 +3817,75 @@ Beispiel: Addition zweier Vektoren \end{center} \subsubsection{Taskparallelität} -\begin{itemize} +\begin{itemize*} \item Unabhängikeit von Teilprozessen $\rightarrow$ Desequentialisierung \item Beispiel: Quicksort -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-0027} \end{center} \subsubsection{Datenparallelität} -\begin{itemize} +\begin{itemize*} \item homogene Datenmenge: Felder, Listen, Dokumentenmenge,… \item Verteilung der Daten \item alle Prozessoren führen gleiches Programm auf jeweils eigenen Daten aus \item Beispiel: Matrixaddition S = A + B -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-0028} \end{center} \subsubsection{Herausforderungen} -\begin{itemize} +\begin{itemize*} \item Zerlegung eines Problems in parallel verarbeitbare Teile - \begin{itemize} - \item Beispiel: Suche in einer Datenbank mit 1 TB Größe - \item Annahme: 100 MB/Sekunde mit einem Prozessor = 175 Minuten - \item bei paralleler Suche durch 10 Prozessoren = 17.5 Minuten - \item Übertragbar auf andere Probleme, z.B. Sortieren, Suche in Graphen? - \end{itemize} + \begin{itemize*} + \item Beispiel: Suche in einer Datenbank mit 1 TB Größe + \item Annahme: 100 MB/Sekunde mit einem Prozessor = 175 Minuten + \item bei paralleler Suche durch 10 Prozessoren = 17.5 Minuten + \item Übertragbar auf andere Probleme, z.B. Sortieren, Suche in Graphen? + \end{itemize*} \item Synchronisation konkurrierender Zugriffe auf gemeinsame Ressourcen - \begin{itemize} - \item Beispiel: Produzent-Konsument-Beziehung - \item Annahme: Datenaustausch über gemeinsame Liste - \item Fragestellungen: Benachrichtigung über neues Element in der Liste, Konsument entnimmt Element während Produzent einfügt - \item Wechselseitiger Ausschluss - \end{itemize} + \begin{itemize*} + \item Beispiel: Produzent-Konsument-Beziehung + \item Annahme: Datenaustausch über gemeinsame Liste + \item Fragestellungen: Benachrichtigung über neues Element in der Liste, Konsument entnimmt Element während Produzent einfügt + \item Wechselseitiger Ausschluss + \end{itemize*} \item außerdem: Fehlersuche, Optimierung -\end{itemize} +\end{itemize*} \subsubsection{Zusammenfassung} -\begin{itemize} +\begin{itemize*} \item Parallele Verarbeitung als wichtiges Paradigma moderner Software \item verschiedene parallele - \begin{itemize} - \item Hardwarearchitekturen und - \item Programmiermodelle - \end{itemize} + \begin{itemize*} + \item Hardwarearchitekturen und + \item Programmiermodelle + \end{itemize*} \item Herausforderungen: - \begin{itemize} - \item Problemzerlegung - \item Synchronisation - \item … - \end{itemize} + \begin{itemize*} + \item Problemzerlegung + \item Synchronisation + \item … + \end{itemize*} \item im Weiteren: konkrete Methoden und Techniken in Erlang und C++ -\end{itemize} +\end{itemize*} \subsection{Grundlagen} \paragraph{Architekturen} SIMD, SMP, NUMA, Cluster, Grid Multiprozessorsysteme: -\begin{itemize} +\begin{itemize*} \item Zugriff über Bus auf gemeinsamen Speicher \item jeder Prozessor mit eigenen Caches -\end{itemize} +\end{itemize*} Multicore-Systeme: -\begin{itemize} +\begin{itemize*} \item mehrere Prozessorkerne auf einem Chip \item Kerne typischerweise mit eigenen L1/L2-Caches und gemeinsamen L3-Cache -\end{itemize} +\end{itemize*} Symetrisch vs. Nicht-symetrisch %| SMP (Symmetric Multi Processing) | NUMA (Non-Uniform Memory-Access) | @@ -4046,62 +3896,62 @@ Symetrisch vs. Nicht-symetrisch Maße zur Leistungsbewertung -Maße für den Laufzeitengewinn durch Parallelisierung -\begin{itemize} +\begin{itemize*} \item $T_n=$ Laufzeit des Programms mit n Prozessoren/Kernen \item Speedup:= $\frac{T_1}{T_n}$ \item Effizienz:= $\frac{Speedup}{n}$ -\end{itemize} +\end{itemize*} Amdahlsches Gesetz $n$ Prozessoren, $p$ parallele Anteile, $s$ serielle Anteile, $p+s=1$ Maximaler Speedup: $Speedup_{max}=\frac{T_1}{T_n}=\frac{1}{s+\frac{p}{n}}$ \paragraph{Grundbegriffe} -\begin{itemize} +\begin{itemize*} \item Prozess := Programm in Ausführung; Ausführungsumgebung für ein Programm; hat eigenen Adressraum; Prozessor kann immer nur einen Prozess ausführen \item Thread ("Faden") := leichtgewichtige Ausführungseinheit oder Kontrollfluss (Folge von Anweisungen) innerhalb eines sich in Ausführung befindlichen Programms; "leichtgewichtig" im Vergleich zu Betriebssystemprozess; Threads eines Prozesses teilen sich Adressraum; Thread kann von CPU oder Core ausgeführt werden \item Shared Memory := Kommunikation (über Variablen im) gemeinsamen Speicher; Prozess kann direkt auf Speicher eines anderen Prozesses zugreifen; erfordert explizite Synchronisation, z.B. über kritische Abschnitte \item Message Passing := Prozesse mit getrennten Adressräumen; Zugriff nur auf eigenen Speicher; Kommunikation durch explizites Senden/Empfangen von Nachrichten; implizite Synchronisation durch Nachrichten \item Parallelisierungsarten - \begin{itemize} - \item Instruktionsparallelität: parallele Ausführung mehrerer Operationen durch eine CPU-Instruktion; explizit: Vektorinstruktionen, SIMD; implizit: Pipelining von Instruktionen - \item Taskparallelität: Ausnutzung inhärenter Parallelität durch simultane Ausführung unabhängiger Aufgaben - \item Datenparallelität: Gemeinsame Operation auf homogener Datenmenge; Zerlegung eines Datensatzes in kleinere Abschnitte - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Instruktionsparallelität: parallele Ausführung mehrerer Operationen durch eine CPU-Instruktion; explizit: Vektorinstruktionen, SIMD; implizit: Pipelining von Instruktionen + \item Taskparallelität: Ausnutzung inhärenter Parallelität durch simultane Ausführung unabhängiger Aufgaben + \item Datenparallelität: Gemeinsame Operation auf homogener Datenmenge; Zerlegung eines Datensatzes in kleinere Abschnitte + \end{itemize*} +\end{itemize*} \subsection{Parallele Programmierung in Erlang - Teil 1} \subsubsection{Unterstützung paralleler Programmierung in Erlang} -\begin{itemize} +\begin{itemize*} \item Leichtgewichtige Prozesse und Message Passing \item SMP-Support (Symmetric Multi Processing) \item Ziele für effiziente Parallelisierung - \begin{itemize} - \item Problem in viele Prozesse zerlegen \newline(aber nicht zu viele …) - \item Seiteneffekte vermeiden \newline (würde Synchronisation erfordern …) - \item Sequentiellen Flaschenhals vermeiden - \newline (Zugriff auf gemeinsame Ressourcen: IO, Registrierung von Prozessen, …) - \begin{center} - \color{orange} Small Messages, Big Computation! \color{black} - \end{center} - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Problem in viele Prozesse zerlegen \newline(aber nicht zu viele …) + \item Seiteneffekte vermeiden \newline (würde Synchronisation erfordern …) + \item Sequentiellen Flaschenhals vermeiden + \newline (Zugriff auf gemeinsame Ressourcen: IO, Registrierung von Prozessen, …) + \begin{center} + \color{orange} Small Messages, Big Computation! \color{black} + \end{center} + \end{itemize*} +\end{itemize*} \subsubsection{Prozesse in Erlang} -\begin{itemize} +\begin{itemize*} \item Erlang VM = Betriebssystemprozess \item Erlang-Prozess = Thread innerhalb der Erlang VM - \begin{itemize} - \item kein Zugriff auf gemeinsame Daten, daher "Prozess" - \end{itemize} + \begin{itemize*} + \item kein Zugriff auf gemeinsame Daten, daher "Prozess" + \end{itemize*} \item jede Erlang-Funktion kann einen Prozess bilden \item Funktion spawn erzeugt einen Prozess, der die Funktion Fun ausführt \newline - \color{blue} Pid \color{black} = \color{green} spawn\color{black}(\color{green} fun \color{blue} Fun\color{black}/0) + \color{blue} Pid \color{black} = \color{green} spawn\color{black}(\color{green} fun \color{blue} Fun\color{black}/0) \item Resultat = Prozessidentifikation Pid, mittels der man dem Prozess Nachrichten schicken kann. \item über self() kann man die eigene Pid ermitteln \item Übergabe von Arghumenten an den Prozess bei der Erzeugung - \newline \color{blue} Pid \color{black} = \color{green} spawn\color{black} (\color{green} fun\color{black} () $\rightarrow$ any\_func(Arg1, Arg2, …)\color{green} end\color{black}) -\end{itemize} + \newline \color{blue} Pid \color{black} = \color{green} spawn\color{black} (\color{green} fun\color{black} () $\rightarrow$ any\_func(Arg1, Arg2, …)\color{green} end\color{black}) +\end{itemize*} \subsubsection{Prozesse in Erlang: Beispiele} \begin{center} \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-029} @@ -4113,14 +3963,14 @@ Maximaler Speedup: $Speedup_{max}=\frac{T_1}{T_n}=\frac{1}{s+\frac{p}{n}}$ \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-030} \end{center} } -\begin{itemize} +\begin{itemize*} \item 4 Betriebssystemthreads (hier 2 Kerne mit Hyperthreading) \item kann mit -smp [disable $\mid$ enable $\mid$ auto] beeinflusst werden \item +S [Anzahl] bestimmt Anzahl der Scheduler - \begin{itemize} - \item sollte nicht größer als Anzahl der Kerne/Prozessoren sein - \end{itemize} -\end{itemize} + \begin{itemize*} + \item sollte nicht größer als Anzahl der Kerne/Prozessoren sein + \end{itemize*} +\end{itemize*} \subsubsection{Scheduler in Erlang} \begin{center} @@ -4128,62 +3978,62 @@ Maximaler Speedup: $Speedup_{max}=\frac{T_1}{T_n}=\frac{1}{s+\frac{p}{n}}$ \end{center} \subsubsection{Message Passing in Erlang: Senden einer Nachricht} \ -\begin{itemize} +\begin{itemize*} \item[1.] \color{blue} Pid ! Message \color{black} - -\end{itemize} -\begin{itemize} + +\end{itemize*} +\begin{itemize*} \item an Prozess Pid wird die Nachricht Message gesendet \item der Prozess muss eine Empfangsoperation ausführen. damit ihn die Nachricht erreichen kann - -\end{itemize} + +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-032} \end{center} -\begin{itemize} +\begin{itemize*} \item trifft eine Nachricht ein, wird versucht, diese mit einem Pattern und ggf. vorhandenen Guard zu "matchen" \item erstes zutreffendes Pattern (inkl. Guard) bestimmt, welcher Ausdruck ausgewertet wird \item trifft kein Pattern zu, wird die Nachricht für spätere Verwendung aufgehoben und Prozess wartet auf die nächste Nachricht ($\rightarrow$ "selective receive") -\end{itemize} +\end{itemize*} \subsubsection{Ein einfacher Echo-Server} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-033} \end{center} \subsubsection{Echo-Server: Erklärungen} -\begin{itemize} +\begin{itemize*} \item Funktion loop() realisiert einen (nur bedingt nützlichen) Echo-Dienst, der jede empfangene Nachricht unverändert an den Absender zurückschickt, bis er nach Empfang von stop endet \item Funktion run() - \begin{itemize} - \item[1.] startet den Echoserver (Zeile 4) - \item[2.] schickt ihm als nächstes eine Nachricht (Zeile 5) - \item[3.] wartet auf eine Antwort (Zeile 6) - \item[4.] gibt diese aus (Zeile 7) - \item[5.] schickt dann stop an den Echoserver (Zeile 9) - \end{itemize} + \begin{itemize*} + \item[1.] startet den Echoserver (Zeile 4) + \item[2.] schickt ihm als nächstes eine Nachricht (Zeile 5) + \item[3.] wartet auf eine Antwort (Zeile 6) + \item[4.] gibt diese aus (Zeile 7) + \item[5.] schickt dann stop an den Echoserver (Zeile 9) + \end{itemize*} \item Aufruf in der Funktion loop() erfolgt endrekursiv, daher wird kein wachsender Aufrufstapel angelegt (Hinweis: grundsätzlich zu beachten, da sonst der Speicherbedarf stetig wächst) -\end{itemize} +\end{itemize*} \subsection{Parallele Programmierung in Erlang - Teil 2} \subsubsection{Ansätze zur Parallelisierung} -\begin{itemize} +\begin{itemize*} \item[] Beispiel: Berechnung einer (zufällig generierten) Liste von Fibonaccizahlen - \begin{itemize} - \item Sequentielle Lösung über lists:map/2 - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Sequentielle Lösung über lists:map/2 + \end{itemize*} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-036} \end{center} \subsubsection{pmap: Parallele Funktionen höherer Ordnung} -\begin{itemize} +\begin{itemize*} \item[] Parallele Variante von lists:map(Fun, list) - \begin{itemize} - \item für jedes Listenelement einen Prozess erzeugen - \item Ergebnisse einsammeln - \end{itemize} -\end{itemize} + \begin{itemize*} + \item für jedes Listenelement einen Prozess erzeugen + \item Ergebnisse einsammeln + \end{itemize*} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-035} \end{center} @@ -4194,54 +4044,54 @@ Maximaler Speedup: $Speedup_{max}=\frac{T_1}{T_n}=\frac{1}{s+\frac{p}{n}}$ \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-034} \end{center} -\begin{itemize} +\begin{itemize*} \item Funktion F aufrufen, catch sorgt für korrekte Behandlung von Fehlern in F \item Ergebnis zusammen mit eigener Pid (self()) an Elternprozess senden -\end{itemize} +\end{itemize*} \color{orange} Einsammeln der Ergebnisse \color{black} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-037} \end{center} -\begin{itemize} +\begin{itemize*} \item Zeile 5: Warten bis Paar (Pid, Ergebniswert) eintrifft \item Zeile 7: Tail ist leer $\rightarrow$ alle Ergebnisse eingetroffen -\end{itemize} +\end{itemize*} \subsubsection{Parallele Berechnung der Fibonacci-Zahlen} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-038} \end{center} \subsubsection{Diskussion} -\begin{itemize} +\begin{itemize*} \item Passende Abstraktion wählen - \begin{itemize} - \item Ist Ordnung der Ergebnisse notwendig? - \item Werden Ergebnisse benötigt? - \end{itemize} + \begin{itemize*} + \item Ist Ordnung der Ergebnisse notwendig? + \item Werden Ergebnisse benötigt? + \end{itemize*} \item Anzahl der parallelen Prozesse - \begin{itemize} - \item Abhängig von Berechnungsmodell, Hardware etc. - \item evtl. pmap mit max. Anzahl gleichzeitiger Prozesse - \end{itemize} + \begin{itemize*} + \item Abhängig von Berechnungsmodell, Hardware etc. + \item evtl. pmap mit max. Anzahl gleichzeitiger Prozesse + \end{itemize*} \item Berechnungsaufwand der Prozesse - \begin{itemize} - \item Berechnung vs. Daten/Ergebnisse senden - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Berechnung vs. Daten/Ergebnisse senden + \end{itemize*} +\end{itemize*} \subsubsection{pmap: Alternative Implementierung} -\begin{itemize} +\begin{itemize*} \item ohne Berücksichtigung der Ordnung der Ergebnismenge \item Zählen für die bereits eingetroffenen Ergebnisse -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-039} \end{center} \subsubsection{Diskussion: Speedup} Bestimmung des Speedups erfordert -\begin{itemize} +\begin{itemize*} \item Zeitmessung \item Kontrolle der genutzten Prozessoren/Cores -\end{itemize} +\end{itemize*} \color{orange} Welchen Einfluss hat die Zahl der erzeugten Prozesse. \color{black} \subsubsection{Speedup: Zeitmessung} Nutzung der Funktion timer:tc/3 @@ -4258,30 +4108,30 @@ ch4\_6:benchmark(ch4\_4, run, 1000). \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-042} \end{center} \color{orange} Achtung: \color{black} -\begin{itemize} +\begin{itemize*} \item Aufwand für Berechnung einer Fibonaccizahl ist nicht konstant \item Zufallszahlen als Eingabe -\end{itemize} +\end{itemize*} \subsubsection{Diskussion: Speedup} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-043} \end{center} \subsubsection{Datenparallelität: Das MapReduce-Paradigma} -\begin{itemize} +\begin{itemize*} \item Parallelisierungsmuster inspiriert von Konzepten funktionaler Programmiersprachen (map,reduce/fold) \item Basis von Big-Data-plattformen wie Hadoop, Spark,… \item Grundidee: - \begin{itemize} - \item map(F, Seq) ? wende Funktion F (als Argument übergeben) auf alle Elemente einer Folge Seq an, - \begin{itemize} - \item z.B. multipliziere jedes Element mit 2 - \end{itemize} - \item reduce(F, Seq) = wende eine Funktion F schrittweise auf die Elemente einer Folge Seq an und produziere einen einzelnen Wert, - \begin{itemize} - \item z.B. die Summer aller Elemente einer Folge - \end{itemize} - \end{itemize} -\end{itemize} + \begin{itemize*} + \item map(F, Seq) ? wende Funktion F (als Argument übergeben) auf alle Elemente einer Folge Seq an, + \begin{itemize*} + \item z.B. multipliziere jedes Element mit 2 + \end{itemize*} + \item reduce(F, Seq) = wende eine Funktion F schrittweise auf die Elemente einer Folge Seq an und produziere einen einzelnen Wert, + \begin{itemize*} + \item z.B. die Summer aller Elemente einer Folge + \end{itemize*} + \end{itemize*} +\end{itemize*} \subsubsection{map in Erlang} \begin{center} @@ -4293,16 +4143,16 @@ ch4\_6:benchmark(ch4\_4, run, 1000). \end{center} \subsubsection{Parallelisierung von map und reduce} \color{orange} map -\color{black} \begin{itemize} +\color{black} \begin{itemize*} \item Funktion F kann unabhängig (=parallel) auf jedes Element angewendet werden \item Partitionieren und Verteilen der Elemente der Folge \item siehe pmap - -\end{itemize} + +\end{itemize*} \color{orange} reduce \color{black} -\begin{itemize} +\begin{itemize*} \item prinzipiell ähnlich, d.h. Funktion F kann auf Paare unabhängig angewandt werden -\end{itemize} +\end{itemize*} \subsubsection{Parallelisierung von reduce} \begin{center} \includegraphics[width=0.5\linewidth]{Assets/Programmierparadigmen-046} @@ -4316,16 +4166,16 @@ ch4\_6:benchmark(ch4\_4, run, 1000). \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-047} \end{center} -\begin{itemize} +\begin{itemize*} \item typische funktionale Notation von Quicksort mit List Comprehensions \item Zeile 2: H dient als Pivotelement -\end{itemize} \ \newline +\end{itemize*} \ \newline Idee: -\begin{itemize} +\begin{itemize*} \item Prozess für das Sortieren der einen Hälfte starten \item Elternprozess kann andere Hälfte sortieren \item rekursive Zerlegung… -\end{itemize} +\end{itemize*} \subsubsection{Parallel Quicksort: Version 1} Quicksort in Erlang @@ -4333,27 +4183,27 @@ Quicksort in Erlang \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-048} \end{center} \subsubsection{Erläuterungen} -\begin{itemize} +\begin{itemize*} \item Zeile 4: Erzeugen eines neuen Prozesses zur Sortierung der "oberen" Hälfte \item Zeile 6-7: Wie bisher \item Zeile 8: Warten auf Empfang der sortierten anderen Hälfte -\end{itemize} +\end{itemize*} Zeitmessung: \begin{center} \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-049} \end{center} \subsubsection{Bewertung} -\begin{itemize} +\begin{itemize*} \item parallele Version 1 ist langsamer! \item mögliche Erklärung: Prozess-Start ist aufwändiger als Sortieren kleiner Teilfolgen \item bessere Variante nach John Hughes: Parallel Programming in Erlang - \begin{itemize} - \item Kontrolle der Granularität für parallele Ausführungen - \item danach Sortieren mit sequenzieller Variante - \item einfache Idee: Anzahl der parallelen Zerlegung begrenzen - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Kontrolle der Granularität für parallele Ausführungen + \item danach Sortieren mit sequenzieller Variante + \item einfache Idee: Anzahl der parallelen Zerlegung begrenzen + \end{itemize*} +\end{itemize*} \begin{center} \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-050} \end{center} @@ -4363,56 +4213,56 @@ Zeitmessung: \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-051} \end{center} \subsubsection{Fazit} -\begin{itemize} +\begin{itemize*} \item \color{orange} leichtgewichtige Prozesse \color{black} als Baustein der Parallelisierung in Erlang \item Prozesskommunikation ausschließlich über \color{orange} Message Passing \color{black} \item \color{orange} funktionaler Charakter \color{black} (u.a. Vermeidung von Seiteneffekten) vereinfacht Parallelisierung deutlich \item \color{orange} Daten- und Taskparallelität \color{black} möglich \item hoher Abstraktionsgrad, aber auch wenig Einflussmöglichkeiten -\end{itemize} +\end{itemize*} \subsection{in Erlang} -\begin{itemize} +\begin{itemize*} \item Leichtgewichtige Prozesse und Message Passing \item SMP-Support \item Ziele für effiziente Parallelisierung: - \begin{itemize} - \item Problem in viele Prozesse zerlegen (aber nicht zu viele ...) - \item Seiteneffekte vermeiden (würde Synchronisation erfordern ...) - \item Sequentiellen Flaschenhals vermeiden (Zugriff auf gemeinsame Ressourcen: IO, Registrierung von Prozessen, ...) - \item Small Messages, Big Computation! - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Problem in viele Prozesse zerlegen (aber nicht zu viele ...) + \item Seiteneffekte vermeiden (würde Synchronisation erfordern ...) + \item Sequentiellen Flaschenhals vermeiden (Zugriff auf gemeinsame Ressourcen: IO, Registrierung von Prozessen, ...) + \item Small Messages, Big Computation! + \end{itemize*} +\end{itemize*} \paragraph{Prozesse in Erlang} -\begin{itemize} +\begin{itemize*} \item Erlang VM = Betriebssystemprozess \item Erlang-Prozess = Thread innerhalb der Erlang VM $\rightarrow$ kein Zugriff auf gemeinsame Daten, daher "Prozess" \item jede Erlang-Funktion kann Prozess bilden \item Funktion spawn erzeugt einen Prozess, der die Funktion Fun ausführt: - \begin{lstlisting} + \begin{lstlisting} Pid = spawn(fun Fun/0) \end{lstlisting} \item Resultat = Prozessidentifikation Pid, mittels der man dem Prozess Nachrichten schicken kann \item über self() kann man die eigene Pid ermitteln \item Übergabe von Argumenten an den Prozess bei der Erzeugung: - \begin{lstlisting} + \begin{lstlisting} Pid = spawn(fun() -> any_func(Arg1, Arg2, ...) end) \end{lstlisting} -\end{itemize} +\end{itemize*} \paragraph{Message Passing in Erlang: Senden einer Nachricht} \begin{lstlisting} Pid ! Message \end{lstlisting} -\begin{itemize} +\begin{itemize*} \item an Prozess Pid wird die Nachricht Message gesendet \item der Prozess muss eine Empfangsoperation ausführen, damit ihn die Nachricht erreichen kann \item trifft eine Nachricht ein, wird versucht, diese mit einem Pattern und ggf. vorhandenen Guard zu "matchen" \item erstes zutreffendes Pattern (inkl. Guard) bestimmt, welcher Ausdruck ausgewertet wird \item trifft kein Pattern zu, wird die Nachricht für spätere Verwendung aufgehoben und Prozess wartet auf die nächste Nachricht ($\rightarrow$ "selective receive") -\end{itemize} +\end{itemize*} \begin{lstlisting}[ language=erlang, @@ -4427,44 +4277,44 @@ end \end{lstlisting} \paragraph{Datenparallelität: Das MapReduce-Paradigma} -\begin{itemize} +\begin{itemize*} \item Parallelisierungsmuster inspiriert von Konzepten funktionaler Programmiersprachen ( map , reduce / fold ) \item Basis von Big-Data-Plattformen wie Hadoop, Spark, ... \item Grundidee: - \begin{itemize} - \item map(F, Seq) = wende Funktion F (als Argument übergeben) auf alle Elemente einer Folge Seq an, - \begin{itemize} - \item Funktion F kann unabhängig (=parallel) auf jedes Element angewendet werden - \item Partitionieren und Verteilen der Elemente der Folge - \item z.B. multipliziere jedes Element mit 2 - \end{itemize} - \item reduce(F, Seq) = wende eine Funktion F schrittweise auf die Element einer Folge Seq an und produziere einen einzelnen Wert, - \begin{itemize} - \item prinzipiell ähnlich zu map(F, Seq), d.h. Funktion F kann auf Paare unabhängig angewendet werden - \item z.B. die Summe aller Elemente der Folge - \end{itemize} - \end{itemize} -\end{itemize} + \begin{itemize*} + \item map(F, Seq) = wende Funktion F (als Argument übergeben) auf alle Elemente einer Folge Seq an, + \begin{itemize*} + \item Funktion F kann unabhängig (=parallel) auf jedes Element angewendet werden + \item Partitionieren und Verteilen der Elemente der Folge + \item z.B. multipliziere jedes Element mit 2 + \end{itemize*} + \item reduce(F, Seq) = wende eine Funktion F schrittweise auf die Element einer Folge Seq an und produziere einen einzelnen Wert, + \begin{itemize*} + \item prinzipiell ähnlich zu map(F, Seq), d.h. Funktion F kann auf Paare unabhängig angewendet werden + \item z.B. die Summe aller Elemente der Folge + \end{itemize*} + \end{itemize*} +\end{itemize*} \paragraph{Fazit} -\begin{itemize} +\begin{itemize*} \item leichtgewichtige Prozesse als Baustein der Parallelisierung in Erlang \item Prozesskommunikation ausschließlich über Message Passing \item funktionaler Charakter (u.a. Vermeidung von Seiteneffekten) vereinfacht Parallelisierung deutlich \item Daten- und Taskparallelität möglich \item hoher Abstraktionsgrad, aber auch wenig Einflussmöglichkeiten -\end{itemize} +\end{itemize*} \subsection{Parallele Programmierung in C++} \subsubsection{Threads in C++} \color{orange} Thread (Faden) \color{black} = leichtgewichtige Ausführungseinheit oder Kontrollfluss (Folge von Anweisungen) innerhalb eines sich in Ausführung befindlichen Programms -\begin{itemize} +\begin{itemize*} \item Threads teilen sich den Adressraum ihres Prozesses \item in C++: Instanzen der Klasse std::thread \item führen eine (initiale) Funktion aus -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-052} \end{center} @@ -4480,21 +4330,21 @@ end \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-054} \end{center} \subsubsection{Parameterübergabe bei Threaderzeugung} -\begin{itemize} +\begin{itemize*} \item über zusätzliche Argumente des thread-Konstruktors \item Vorsicht bei Übergabe von Referenzen, wenn Elternthread vor dem erzeugten Thread beendet wird - -\end{itemize} + +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-056} \end{center} \subsubsection{Warten auf Threads} -\begin{itemize} +\begin{itemize*} \item t.join() wartet auf Beendigung des Threads t \item blockiert aktuellen Thread \item ohne join() keine Garantie, dass t zur Ausführung kommt \item Freigabe der Ressourcen des Threads -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-057} \end{center} @@ -4502,65 +4352,65 @@ end \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-058} \end{center} \subsubsection{Hintergrundthreads} -\begin{itemize} +\begin{itemize*} \item Threads können auch im Hintergrund laufen, ohne, dass auf Ende gewartet werden muss \item "abkoppeln" durch detach() \item Thread läuft danach unter Kontrolle des C++-Laufzeitsystems, join nicht mehr möglich -\end{itemize} +\end{itemize*} \subsubsection{Threadidentifikation} -\begin{itemize} +\begin{itemize*} \item Threadidentifikator vom Typ std::thread::id \item Ermittlung über Methode get\_id() -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-059} \end{center} \subsubsection{Berechnung von Fibonacci-Zahlen in C++} -\begin{itemize} +\begin{itemize*} \item rekursive und nichtrekursive Variante möglich -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-060} \end{center} \subsubsection{Parallele Berechnung von Fibonacci-Zahlen} -\begin{itemize} +\begin{itemize*} \item einfachste Lösung (ähnlich zu Erlang): pro Zahl ein Thread -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-061} \end{center} \subsubsection{Erläuterungen} -\begin{itemize} +\begin{itemize*} \item Zeile 1: Feld der Threads \item Zeile 2: Feld für Ergebniswerte \item Zeile 5: Zufallszahl erzeugen \item Zeilen 6-7 Thread zur Berechnung der Fibonacci-Zahl erzeugen und Ergebnis im Feld speichern \item Zeile 10-11: Warten auf Beendigung der Threads (std::mem\_fn = Wrapper für Zeiger auf Member-Funktion) \item \color{orange} aber: \color{black} - \begin{itemize} - \item Zugriff auf gemeinsame Ressource (results)! - \item Anzahl Fibonaccizahlen = Anzahl Threads - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Zugriff auf gemeinsame Ressource (results)! + \item Anzahl Fibonaccizahlen = Anzahl Threads + \end{itemize*} +\end{itemize*} \subsubsection{parallel-for in C++} -\begin{itemize} +\begin{itemize*} \item Unterstützung durch Higher-Level-APIs und Frameworks -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-062} \end{center} \subsubsection{Kontrolle der Anzahl der Threads} -\begin{itemize} +\begin{itemize*} \item Erzeugung von Threads ist mit Kosten verbunden \item begrenzte Anzahl von Hardwarethreads (Anzahl Cores, Hyperthreading) \item Ermittlung der Anzahl der unterstützten Hardwarethreads -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-063} \end{center} -\begin{itemize} +\begin{itemize*} \item Nutzung für Implementierung von Threadpools, Task Libraries, … -\end{itemize} +\end{itemize*} \subsubsection{Probleme nebenläufiger Ausführung} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-064} @@ -4574,67 +4424,67 @@ Ausgabe: \end{center} \color{orange} Race Conditions \color{black}(Wettlaufsituationen) := Ergebnis nebenläufiger Ausführung auf gemeinsamen Zustand (hier: Ausgabekanal) hängt vom zeitlichen Verhalten der Einzeloperationen ab \subsubsection{Wechselseitiger Ausschluss} -\begin{itemize} +\begin{itemize*} \item \color{orange} kritischer Abschnitt\color{black}: Programmabschnitt in einem Thread, in dem auf eine gemeinsame Ressource (Speicher etc.) zugegriffen wird und der nicht parallel (oder zeitlich verzahnt) zu einem anderen Thread ausgeführt werden darf \item Lösung durch \color{orange} wechselseitigen Ausschluss \color{black} (engl. mutual exclusion = mutex) -\end{itemize} +\end{itemize*} \subsubsection{Mutex in C++} -\begin{itemize} +\begin{itemize*} \item Instanz der Klasse std::mutex \item Methoden zum Sperren (lock) und Freigeben (unlock) -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-067} \end{center} \subsubsection{Mutex-Varianten} -\begin{itemize} +\begin{itemize*} \item mutex: Standard-Mutex für exklusiven Zugriff \item timed\_mutex: Mutex mit Timeout für Warten (try\_lock\_for()) \item recursive\_mutex:rekursives Mutex - erlaubt mehrfaches Sperren durch einen Thread, z.B. für rekursive Aufrufe \item recursive\_timed\_mutex: rekursives Mutex mit Timeout \item shared\_mutex: Mutex, das gemeinsamen Zugriff (lock\_shared()) mehrerer Threads oder exklusiven Zugriff (lock()) ermöglicht \item shared\_timed\_mutex: Mutex mit Timeout und gemeinsamen Zugriff -\end{itemize} +\end{itemize*} \subsubsection{Lock Guards} -\begin{itemize} +\begin{itemize*} \item Vereinfachung der Nutzung von Mutexen durch RAII ("Ressourcenbelegung ist Initialisierung") \item Konstruktor = lock \item Destruktor = unlock -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-068} \end{center} \subsubsection{Lock Gurads und Locks} -\begin{itemize} +\begin{itemize*} \item std::unique\_lock erweiterte Variante von lock\_guards, vermeidet aber sofortiges Sperren \item std::lock erlaubt gleichzeitiges deadlock-freies Sperren von 2 Mutexen \item Sperrstrategien: u.a. - \begin{itemize} - \item std::try\_to\_lock versucht Sperre ohne Blockierung zu setzen - \item std::adopt\_lock versucht nicht, ein zweites Mal zu sperren, wenn bereits durch den aktuellen Thread gesperrt - \end{itemize} -\end{itemize} + \begin{itemize*} + \item std::try\_to\_lock versucht Sperre ohne Blockierung zu setzen + \item std::adopt\_lock versucht nicht, ein zweites Mal zu sperren, wenn bereits durch den aktuellen Thread gesperrt + \end{itemize*} +\end{itemize*} \subsubsection{Atomare Datentypen} -\begin{itemize} +\begin{itemize*} \item std::atomic\_flag = sperrfreier, atomarer Datentyp: - \begin{itemize} - \item clear() setzt den Wert auf false - \item test\_and\_set() setzt den Wert atomar auf true und liefert den vorherigen Wert - \end{itemize} + \begin{itemize*} + \item clear() setzt den Wert auf false + \item test\_and\_set() setzt den Wert atomar auf true und liefert den vorherigen Wert + \end{itemize*} \item std::atomic = mächtigere Variante, erlaubt explizites Setzen - \begin{itemize} - \item operator= atomare Wertzuweisung - \item load() liefert den aktuellen Wert - \item read-modify-write-Operation (siehe später) - \end{itemize} + \begin{itemize*} + \item operator= atomare Wertzuweisung + \item load() liefert den aktuellen Wert + \item read-modify-write-Operation (siehe später) + \end{itemize*} \item std::atomic = generische Variante für weitere Datentypen -\end{itemize} +\end{itemize*} \subsubsection{Synchronisation über atomare Variable} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-069} \end{center} \subsubsection{Erläuterungen} -\begin{itemize} +\begin{itemize*} \item Zeile 1: gemeinsam genutzte Liste - erfordert synchronisierten Zugriff \item Zeile 2: atomare boolsche Variable ready \item Zeile 4/12: Konsument/Produzent-Threads @@ -4642,31 +4492,31 @@ Ausgabe: \item Zeile 6-7 kurz warten und neu versuchen \item Zeile 8-9/13 Zugriff auf gemeinsame Liste \item Zeile 14: atomares Setzen der Variablen ready -\end{itemize} +\end{itemize*} \subsubsection{Taskparallelität: Die 5 speisenden Philosophen} -\begin{itemize} +\begin{itemize*} \item fünf Philosophen teilen sich eine Schüssel Sphagetti \item fünf Gabeln, je eine zwischen zwei Philosophen \item Philosoph kann nur mit zwei benachbarten Gabeln essen \item Gabeln werden nur nach dem Essen zurückgelegt \item Philosoph durchläuft Zyklus von Zuständen: denken $\rightarrow$ hungrig $\rightarrow$ essen $\rightarrow$ denken $\rightarrow$ etc. -\end{itemize} +\end{itemize*} \subsubsection{Das Problem mit den Philosophen} -\begin{itemize} +\begin{itemize*} \item Jeder greift die linke Gabel \item und wartet auf die rechte Gabel \item … und wartet … -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.3\linewidth]{Assets/Programmierparadigmen-070} \end{center} \color{orange} \textbf{Verklemmung!} \color{black} \subsubsection{Lösungsidee} -\begin{itemize} +\begin{itemize*} \item immer beide Gabeln aufnehmen, dh. wenn nur eine Gabel verfügbar ist: liegen lassen und warten \item synchronisierter Zugriff auf Gablen, dh. in einem kritischen Abschnitt unter gegenseitige Ausschluss \item Wecken von wartenden Philosophen -\end{itemize} +\end{itemize*} \subsubsection{Verklemmungsfreies Sperren} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-071} @@ -4701,9 +4551,9 @@ Hilfsmethode für zufällige Wartezeit in Millisekunden \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-078} \end{center} \subsubsection{Das Leben eines Philosophen} -\begin{itemize} +\begin{itemize*} \item Zur Erinnerung: überladener ()-Operator eines Objekts definiert auszuführende Funktion eines Threads -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-079} \end{center} @@ -4712,43 +4562,43 @@ Hilfsmethode für zufällige Wartezeit in Millisekunden \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-080} \end{center} \subsubsection{Das Dinner beginnt} -\begin{itemize} +\begin{itemize*} \item Beginn (und Ende) des Dinners über atomare Variable signalisieren \item Philosophen-Threads arbeiten ihre operator()()-Methode ab -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-081} \end{center} \subsubsection{Fazit} -\begin{itemize} +\begin{itemize*} \item Philosophenproblem: klassisches Problem der Informatik zur Demonstration von Nebenläufigkeit und Verklemmung \item von Edsger W. Dijkstra formuliert \item betrachte C++ Lösung illustriert - \begin{itemize} - \item Nebenläufigkeit durch Threads - \item Synchronisation über Mutexe - \item verklemmungsfreies Sperren - \end{itemize} + \begin{itemize*} + \item Nebenläufigkeit durch Threads + \item Synchronisation über Mutexe + \item verklemmungsfreies Sperren + \end{itemize*} \item moderene C++ Sprachversion vereinfacht Programmierung gegenüber Low-Level-API auf Betriebssystemebene (z.B. pthreads) -\end{itemize} +\end{itemize*} \subsubsection{Weitere Möglichkeiten der Thread-Interaktion} -\begin{itemize} +\begin{itemize*} \item bisher: - \begin{itemize} - \item Mutexe und Locks - \item atomare Variablen - \end{itemize} + \begin{itemize*} + \item Mutexe und Locks + \item atomare Variablen + \end{itemize*} \item typischer Anwendungsfall: Warten auf Ereignis / Setzen eines Flags -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-082} \end{center} \subsubsection{Bedingungsvariablen} -\begin{itemize} +\begin{itemize*} \item Thread wartet, bis Bedingung erfüllt ist \item Erfüllung der Bedingung wird durch anderen Thread angezeigt (notify) $\rightarrowtail$ "Aufwecken" des wartenden Threads \item notwendig: synchronisierter Zugriff über Mutex -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-083} \end{center} @@ -4758,65 +4608,65 @@ Hilfsmethode für zufällige Wartezeit in Millisekunden \subsubsection{Thread-sichere Datenstrukturen} \color{orange} Thread-Sicherheit:= \color{black} eine Komponente kann gleichzeitig von verschiedenen Programmbereichen Threads mehrfach ausgeführt werden, ohne dass diese sich gegenseitig behindern \newline \newline verschiedene Varianten: -\begin{itemize} +\begin{itemize*} \item Standard-Datenstruktur + über Mutexe/Sperren synchronisierte Zugriffe \item Integration der Sperren in die Datenstruktur \item Sperr-freie Datenstrukturen: nicht blockierend, Vermeidung von Sperren, z.B. durch Compare/Exchange-Operationen -\end{itemize} +\end{itemize*} \subsubsection{Anforderungen} -\begin{itemize} +\begin{itemize*} \item mehrere Threads können gleichzeitig auf die Datenstruktur zugreifen \item kein Thread sieht (Zwischen-)Zustand, bei dem Invarianten der Datenstruktur durch einen anderen Thread (kurzzeitig) verletzt ist \item Vermeidung von Wettlaufsituationen \item Vermeidung von Verklemmungen \item korrekte Behandlung von Ausnahmen (Fehlern) -\end{itemize} +\end{itemize*} \subsubsection{Thread-sichere Queue} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-085} \end{center} -\begin{itemize} +\begin{itemize*} \item Zeilen 1,2,6: Kapselung der std::queue-Klasse \item Zeile 4: Mutex für exklusiven Zugriff \item Zeile 5: Bedingungsvariable für Warten -\end{itemize} +\end{itemize*} \subsubsection{Thread-sichere Queue: Methode push} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-086} \end{center} -\begin{itemize} +\begin{itemize*} \item Zeile 2: Lock Guard sichert exklusiven Zugriff \item Zeile 3: Element an die Queue anhängen \item Zeile 4: Aufwecken von eventuell wartenden Threads -\end{itemize} +\end{itemize*} \subsubsection{Thread-sichere Queue: Methode waiting\_pop} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-087} \end{center} -\begin{itemize} +\begin{itemize*} \item Zeile 2: Lock Guard sichert exklusiven Zugriff \item Zeile 3: Warten bis Queue nicht mehr leer ist \item Zeilen 4,5: erstes Element aus der Queue entnehmen -\end{itemize} +\end{itemize*} \subsubsection{async, future und promise} -\begin{itemize} +\begin{itemize*} \item std::future - Resultat einer asynchronen Berechnung, d.h. einer Berechnung die erst noch stattfindet \item std::async() - asynchrones Starten eines Tasks - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-088} - \end{center} + \begin{center} + \centering + \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-088} + \end{center} \item std::promise - erlaubt Wert zu setzen, wenn der aktuelle Thread beendet ist, of in Kombination mit std::future eingesetzt \item future = Ergbenisobjekt, promise = Ergebnisproduzent -\end{itemize} +\end{itemize*} \subsubsection{Future} -\begin{itemize} +\begin{itemize*} \item Methoden zum - \begin{itemize} - \item Warten auf das Ende des Tasks (wait(), wait\_for()) - \item Ergebnis lesen (get()) - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Warten auf das Ende des Tasks (wait(), wait\_for()) + \item Ergebnis lesen (get()) + \end{itemize*} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-089} \end{center} @@ -4825,41 +4675,41 @@ verschiedene Varianten: \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-090} \end{center} \subsubsection{Deklarative Parallelisierung mit OpenMP} -\begin{itemize} +\begin{itemize*} \item Programmierschnittstelle für Parallelisierung in C/C++/Fortran \item Programmiersprachenerweiterung durch Direktiven \item in C/C++: \#pragma omp … \item zusätzliche Bibliotheksfunktionen: \#include \item aktuelle Version 5.0 \item Unterstützung in gcc und clang - \begin{itemize} - \item vollständig 4.5, partiell 5.0 - \item Nutzung über Compilerflag - fopenmp - \end{itemize} + \begin{itemize*} + \item vollständig 4.5, partiell 5.0 + \item Nutzung über Compilerflag - fopenmp + \end{itemize*} \item beschränkt auf Architekturen mit gemeinsamen Speicher -\end{itemize} +\end{itemize*} \subsubsection{Programmiermodell} -\begin{itemize} +\begin{itemize*} \item Master-Thread und mehrere Worker-Threads (Anzahl typischerweise durch OpenMP-Laufzeitsystem bestimmt) \item über parallel-Direktive kann Arbeit in einem Programmabschnitt auf Worker-Threads aufgeteilt werden \item Ende des parallelen Abschnitts $\rightarrowtail$ implizite Synchronisation \item Fortsetzung des Master-Threads -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.2\linewidth]{Assets/Programmierparadigmen-091} \end{center} \subsubsection{Hello World! mit OpenMP} -\begin{itemize} +\begin{itemize*} \item der dem pragma folgende Block wird parallel von allen Threads ausgeführt -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-092} \end{center} \subsubsection{Schleifenparallelisierung} -\begin{itemize} +\begin{itemize*} \item parallele Ausführung einer Schleife: jedem Thread wird ein Teil der Iterationen zugewiesen \item für for-Schleifgen mit eingeschränkter Syntax (ganzzahlige Schleifenvariablen, Operatoren auf Schleifenvariablen) und für STL-Iteratoren -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-093} \end{center} @@ -4876,92 +4726,92 @@ bedingte Parallelisierung \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-095} \end{center} \subsubsection{Aufteilung des Iterationsbereichs} -\begin{itemize} +\begin{itemize*} \item Iterationsbereich kann auf verschiedene Weise auf Threads aufgeteilt werden \item Beeinflussung durch schedule-Direktive - \begin{itemize} - \item schedule(auto): Default - implementierungsspezifisch - \item schedule(static,n): statische Round-Robin-Verteilung - Bereiche der Größe n (Angabe von n ist optional) - \item schedule(dynamic, n): dynamische Verteilung nach Bedarf - \item schedule(guided, n): Verteilung nach Bedarf und proportional zur Restarbeit - \item … - \end{itemize} -\end{itemize} + \begin{itemize*} + \item schedule(auto): Default - implementierungsspezifisch + \item schedule(static,n): statische Round-Robin-Verteilung - Bereiche der Größe n (Angabe von n ist optional) + \item schedule(dynamic, n): dynamische Verteilung nach Bedarf + \item schedule(guided, n): Verteilung nach Bedarf und proportional zur Restarbeit + \item … + \end{itemize*} +\end{itemize*} \subsubsection{Geschachtelte Schleifen} -\begin{itemize} +\begin{itemize*} \item Parallelisierung mit \textbf{parallel for} beeinflusst nur äußere Schleife \item collapse(n) gibt an, dass n Schleifen in einem gemeinsamen Iterationsbereich zusammengefasst, und auf die Threads verteilt werden sollen \item Beispiel: Matrizenmultiplikation -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-096} \end{center} \subsubsection{Synchronisation} -\begin{itemize} +\begin{itemize*} \item Direktiven für parallele Ausführung - \begin{itemize} - \item \textbf{\#pragma omp single/master} Abschnitt wird nur durch einen/den Master-Thread ausgeführt - \item \textbf{\#pragma omp critical} kritischer Abschnitt - \item \textbf{\#pragma omp barrier} Warten auf alle Worker-Threads - \item \textbf{\#pragma omp atomic} kritischer Abschnitt - ZUgriff auf gemeinsame Variable (z.B. Zähler) - \end{itemize} + \begin{itemize*} + \item \textbf{\#pragma omp single/master} Abschnitt wird nur durch einen/den Master-Thread ausgeführt + \item \textbf{\#pragma omp critical} kritischer Abschnitt + \item \textbf{\#pragma omp barrier} Warten auf alle Worker-Threads + \item \textbf{\#pragma omp atomic} kritischer Abschnitt - ZUgriff auf gemeinsame Variable (z.B. Zähler) + \end{itemize*} \item Speicherklauseln für Variablen - \begin{itemize} - \item \textbf{shared} für alle Threads sichtbar/änderbar - \item \textbf{private} jeder Thread hat eigene Kopie der Daten, wird nicht außerhalb initialisiert - \item \textbf{reduction} private Daten, die am Ende des Abschnitts zu globalem Wert zusammengefasst werden - \item \textbf{firstprivate/lastprivate} privat - initialisiert mit letztem Wert vor dem Abschnitt / Wert des letzten Threads der Iteration wird zurückgegeben - \end{itemize} -\end{itemize} + \begin{itemize*} + \item \textbf{shared} für alle Threads sichtbar/änderbar + \item \textbf{private} jeder Thread hat eigene Kopie der Daten, wird nicht außerhalb initialisiert + \item \textbf{reduction} private Daten, die am Ende des Abschnitts zu globalem Wert zusammengefasst werden + \item \textbf{firstprivate/lastprivate} privat - initialisiert mit letztem Wert vor dem Abschnitt / Wert des letzten Threads der Iteration wird zurückgegeben + \end{itemize*} +\end{itemize*} \subsubsection{Parallele Abschnitte} -\begin{itemize} +\begin{itemize*} \item Zuweisung von Programmabschnitten zu Threads $\rightarrowtail$ statische Parallelität \item geeignet z.B. für rekursive Aufrufe -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-097} \end{center} \subsubsection{Task-Programmierung mit OpenMP} -\begin{itemize} +\begin{itemize*} \item seit OpenMP 3.0 Unterstützung von Tasks, die - \begin{itemize} - \item reihum Threads zugewiesen werden - \item an beliebiger Stelle definiert werden können - \item von beliebigem Thread definiert werden kann - \end{itemize} -\end{itemize} + \begin{itemize*} + \item reihum Threads zugewiesen werden + \item an beliebiger Stelle definiert werden können + \item von beliebigem Thread definiert werden kann + \end{itemize*} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-098} \end{center} \subsubsection{Fazit} -\begin{itemize} +\begin{itemize*} \item C++ bietet weitreichende und mächtige Konzepte zur Parallelisierung - \begin{itemize} - \item von Basiskontrolle wie Threads und Synchronisationsprimitiven (u.a. Mutexe) - \item …über höherwertige Abstraktionen wie async, Features und Promises - \item bis hin zu deklarativen Ansätzen wie OpenMP - \end{itemize} + \begin{itemize*} + \item von Basiskontrolle wie Threads und Synchronisationsprimitiven (u.a. Mutexe) + \item …über höherwertige Abstraktionen wie async, Features und Promises + \item bis hin zu deklarativen Ansätzen wie OpenMP + \end{itemize*} \item alle Formen von Parallelität (Instruktions-, Daten-, und Taskparallelität) möglich \item aber anspruchsvolle Programmierung \item erleichtert durch zusätzliche Bibliotheken und Frameworks wie Parallel STL, TBB, … -\end{itemize} +\end{itemize*} \subsection{in C++} Thread ("Faden") := leichtgewichtige Ausführungseinheit oder Kontrollfluss (Folge von Anweisungen) innerhalb eines sich in Ausführung befindlichen Programms -\begin{itemize} +\begin{itemize*} \item Threads teilen sich den Adressraum des ihres Prozesses \item in C++: Instanzen der Klasse std::thread \item führen eine (initiale) Funktion aus -\end{itemize} +\end{itemize*} Parameter-Übergabe bei Thread-Erzeugung -\begin{itemize} +\begin{itemize*} \item über zusätzliche Argumente des thread -Konstruktors \item Vorsicht bei Übergabe von Referenzen, wenn Eltern-Thread vor dem erzeugten Thread beendet wird -\end{itemize} +\end{itemize*} \begin{lstlisting}[ language=C++, showspaces=false, @@ -4977,12 +4827,12 @@ t.join(); \end{lstlisting} Warten auf Threads -\begin{itemize} +\begin{itemize*} \item t.join() wartet auf Beendigung des Threads t \item blockiert aktuellen Thread \item ohne join() keine Garantie, dass t zur Ausführung kommt \item Freigabe der Ressourcen des Threads -\end{itemize} +\end{itemize*} \begin{lstlisting}[ language=C++, showspaces=false, @@ -4993,59 +4843,59 @@ t.join(); \end{lstlisting} Hintergrund-Threads -\begin{itemize} +\begin{itemize*} \item Threads können auch im Hintergrund laufen, ohne dass auf Ende gewartet werden muss \item "abkoppeln" durch detach() \item Thread läuft danach unter Kontrolle des C++-Laufzeitsystems, join nicht mehr möglich -\end{itemize} +\end{itemize*} Thread-Identifikation -\begin{itemize} +\begin{itemize*} \item Thread-Identifikator vom Typ std::thread::id \item Ermittlung über Methode get\_id() -\end{itemize} +\end{itemize*} Kontrolle der Anzahl der Threads -\begin{itemize} +\begin{itemize*} \item Erzeugung von Threads ist mit Kosten verbunden \item begrenzte Anzahl von Hardware-Threads (Anzahl Cores, Hyperthreading) \item Ermittlung der Anzahl der unterstützen Hardwarethreads - \begin{lstlisting} + \begin{lstlisting} std::thread::hardware_concurrency() \end{lstlisting} \item Nutzung für Implementierung von Threadpools, Task Libraries, ... -\end{itemize} +\end{itemize*} Probleme nebenläufiger Ausführung -\begin{itemize} +\begin{itemize*} \item Race Conditions (Wettlaufsituation) := Ergebnis nebenläufiger Ausführung auf gemeinsamen Zustand (hier: Ausgabekanal) hängt vom zeitlichen Verhalten der Einzeloperationen ab \item kritischer Abschnitt: Programmabschnitt in einem Thread, in dem auf eine gemeinsame Ressource (Speicher etc.) zugegriffen wird und der nicht parallel (oder zeitlich verzahnt) zu einem anderen Thread ausgeführt werden darf \item Lösung durch wechselseitigen Ausschluss (engl. mutual exclusion = mutex) - \begin{itemize} - \item Instanz der Klasse std::mutex - \item Methoden zum Sperren ( lock ) und Freigeben ( unlock ) - \item 'mutex' : Standard-Mutex für exklusiven Zugriff - \item 'timed\_mutex' : Mutex mit Timeout für Warten ( try\_lock\_for() ) - \item 'recursive\_mutex' : rekursives Mutex - erlaubt mehrfaches Sperren durch einen Thread, z.B. für rekursive Aufrufe - \item 'recursive\_timed\_mutex' : rekursives Mutex mit Timeout - \item 'shared\_mutex' : Mutex, das gemeinsamen Zugriff ( lock\_shared() ) mehrerer Threads oder exklusiven Zugriff ( lock() ) ermöglicht - \item 'shared\_timed\_mutex' : Mutex mit Timeout und gemeinsamen Zugriff - \end{itemize} + \begin{itemize*} + \item Instanz der Klasse std::mutex + \item Methoden zum Sperren ( lock ) und Freigeben ( unlock ) + \item 'mutex' : Standard-Mutex für exklusiven Zugriff + \item 'timed\_mutex' : Mutex mit Timeout für Warten ( try\_lock\_for() ) + \item 'recursive\_mutex' : rekursives Mutex - erlaubt mehrfaches Sperren durch einen Thread, z.B. für rekursive Aufrufe + \item 'recursive\_timed\_mutex' : rekursives Mutex mit Timeout + \item 'shared\_mutex' : Mutex, das gemeinsamen Zugriff ( lock\_shared() ) mehrerer Threads oder exklusiven Zugriff ( lock() ) ermöglicht + \item 'shared\_timed\_mutex' : Mutex mit Timeout und gemeinsamen Zugriff + \end{itemize*} \item Lock Guards - \begin{itemize} - \item Vereinfachung der Nutzung von Mutexen durch RAII ("Ressourcenbelegung ist Initialisierung") - \item Konstruktor = lock - \item Destruktor = unlock - \item std::unique\_lock erweiterte Variante von lock\_guard, vermeidet aber sofortiges Sperren - \item std::lock : erlaubt gleichzeitiges deadlock-freies Sperren von 2 Mutexen - \item Sperrstrategien: u.a. - \begin{itemize} - \item std::try\_to\_lock versucht Sperre ohne Blockierung zu setzen - \item std::adopt\_lock versucht nicht, ein zweites Mal zu sperren, wenn bereits durch den aktuellen Thread gesperrt - \end{itemize} - \end{itemize} - \begin{lstlisting}[ + \begin{itemize*} + \item Vereinfachung der Nutzung von Mutexen durch RAII ("Ressourcenbelegung ist Initialisierung") + \item Konstruktor = lock + \item Destruktor = unlock + \item std::unique\_lock erweiterte Variante von lock\_guard, vermeidet aber sofortiges Sperren + \item std::lock : erlaubt gleichzeitiges deadlock-freies Sperren von 2 Mutexen + \item Sperrstrategien: u.a. + \begin{itemize*} + \item std::try\_to\_lock versucht Sperre ohne Blockierung zu setzen + \item std::adopt\_lock versucht nicht, ein zweites Mal zu sperren, wenn bereits durch den aktuellen Thread gesperrt + \end{itemize*} + \end{itemize*} + \begin{lstlisting}[ language=C++, showspaces=false, basicstyle=\ttfamily @@ -5063,23 +4913,23 @@ std::lock_guard guard(my_mtx); return data.front(); } \end{lstlisting} - + \item Atomare Datentypen - \begin{itemize} - \item std::atomic\_flag = sperrfreier, atomarer Datentyp: - \begin{itemize} - \item clear() setzt den Wert auf false - \item test\_and\_set() setzt den Wert atomar auf true und liefert den vorherigen Wert - \end{itemize} - \item std::atomic = mächtigere Variante, erlaubt explizites Setzen - \begin{itemize} - \item operator= atomare Wertzuweisung - \item load() liefert den aktuellen Wert - \item read-modify-write-Operation (siehe später) - \end{itemize} - \item std::atomic = generische Variante für weitere Datentypen - \end{itemize} -\end{itemize} + \begin{itemize*} + \item std::atomic\_flag = sperrfreier, atomarer Datentyp: + \begin{itemize*} + \item clear() setzt den Wert auf false + \item test\_and\_set() setzt den Wert atomar auf true und liefert den vorherigen Wert + \end{itemize*} + \item std::atomic = mächtigere Variante, erlaubt explizites Setzen + \begin{itemize*} + \item operator= atomare Wertzuweisung + \item load() liefert den aktuellen Wert + \item read-modify-write-Operation (siehe später) + \end{itemize*} + \item std::atomic = generische Variante für weitere Datentypen + \end{itemize*} +\end{itemize*} \paragraph{Verklemmungsfreies Sperren} \begin{lstlisting}[ @@ -5103,18 +4953,18 @@ std::lock(lk1, lk2); \end{lstlisting} \paragraph{Thread-sichere Datenstrukturen} -\begin{itemize} +\begin{itemize*} \item Thread-Sicherheit := eine Komponente kann gleichzeitig von verschiedenen Programmbereichen (Threads) mehrfach ausgeführt werden, ohne dass diese sich gegenseitig behindern \item verschiedene Varianten: - \begin{itemize} - \item Standard-Datenstruktur + über Mutexe/Sperren synchronisierte Zugriffe - \item Integration der Sperren in die Datenstruktur - \item Sperr-freie Datenstrukturen: nicht-blockierend, Vermeidung von Sperren, z.B. durch Compare/Exchange-Operationen - \end{itemize} + \begin{itemize*} + \item Standard-Datenstruktur + über Mutexe/Sperren synchronisierte Zugriffe + \item Integration der Sperren in die Datenstruktur + \item Sperr-freie Datenstrukturen: nicht-blockierend, Vermeidung von Sperren, z.B. durch Compare/Exchange-Operationen + \end{itemize*} \item async , future und promise \item std::future - Resultat einer asynchronen Berechnung, d.h. einer Berechnung die erst noch stattfindet \item std::async() - asynchrones Starten eines Tasks - \begin{lstlisting}[ + \begin{lstlisting}[ language=C++, showspaces=false, basicstyle=\ttfamily @@ -5127,20 +4977,20 @@ std::lock(lk1, lk2); \end{lstlisting} \item std::promise - erlaubt Wert zu setzen, wenn der aktuelle Thread beendet ist; oft in Kombination mit std::future eingesetzt \item future = Ergebnisobjekt, promise = Ergebnisproduzent - \begin{itemize} - \item Warten auf Ende des Tasks (wait(), wait\_for()) - \item Ergebnis lesen (get()) - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Warten auf Ende des Tasks (wait(), wait\_for()) + \item Ergebnis lesen (get()) + \end{itemize*} +\end{itemize*} \subsubsection{Programmiermodell} -\begin{itemize} +\begin{itemize*} \item Master-Thread und mehrere Worker-Threads (Anzahl typischerweise durch OpenMP-Laufzeitsystem bestimmt) \item über parallel -Direktive kann Arbeit in einem Programmabschnitt auf Worker-Threads aufgeteilt werden \item Ende des parallelen Abschnitts $\rightarrow$ implizite Synchronisation $\rightarrow$ Fortsetzung des Master-Threads \item der dem 'pragma' folgende Block wird parallel von allen Threads ausgeführt - \begin{lstlisting}[ + \begin{lstlisting}[ language=C++, showspaces=false, basicstyle=\ttfamily @@ -5159,7 +5009,7 @@ std::lock(lk1, lk2); } \end{lstlisting} \item Schleifenparallelisierung: jedem Thread wird ein Teil der Iteration zugewiesen (beeinflusst nur äußere Schleife) - \begin{lstlisting}[ + \begin{lstlisting}[ language=C++, showspaces=false, basicstyle=\ttfamily @@ -5168,46 +5018,46 @@ std::lock(lk1, lk2); \#pragma omp parallel for for (int i = 0; i < 20; i++) {... \end{lstlisting} - \begin{itemize} - \item collapse(n) gibt an, dass n Schleifen in einem gemeinsamen Iterationsbereich zusammengefasst und auf die Threads verteilt werden sollen - \begin{lstlisting} + \begin{itemize*} + \item collapse(n) gibt an, dass n Schleifen in einem gemeinsamen Iterationsbereich zusammengefasst und auf die Threads verteilt werden sollen + \begin{lstlisting} #pragma omp parallel for collapse(3) \end{lstlisting} - \end{itemize} + \end{itemize*} \item Beeinflussung der Thread Anzahl - \begin{itemize} - \item maximale Anzahl: - \begin{lstlisting} + \begin{itemize*} + \item maximale Anzahl: + \begin{lstlisting} #pragma omp parallel for num_threads(8) \end{lstlisting} - \item bedingte Parallelisierung: - \begin{lstlisting} + \item bedingte Parallelisierung: + \begin{lstlisting} #pragma omp parallel for if(i>50) \end{lstlisting} - \end{itemize} + \end{itemize*} \item Aufteilung des Iterationsbereichs; Beeinflussung durch schedule -Direktive - \begin{itemize} - \item schedule(auto): Default - implementierungsspezifisch - \item schedule(static, n): statische Round-Robin-Verteilung - Bereiche der Größe n (Angabe von n ist optional) - \item schedule(dynamic, n): dynamische Verteilung nach Bedarf - \item schedule(guided, n): Verteilung nach Bedarf und proportional zur Restarbeit - \end{itemize} + \begin{itemize*} + \item schedule(auto): Default - implementierungsspezifisch + \item schedule(static, n): statische Round-Robin-Verteilung - Bereiche der Größe n (Angabe von n ist optional) + \item schedule(dynamic, n): dynamische Verteilung nach Bedarf + \item schedule(guided, n): Verteilung nach Bedarf und proportional zur Restarbeit + \end{itemize*} \item Direktiven für parallele Ausführung - \begin{itemize} - \item '\#pragma omp single/master' Abschnitt wird nur durch einen/den Master-Thread ausgeführt - \item '\#pragma omp critical' kritischer Abschnitt - \item '\#pragma omp barrier' Warten auf alle Worker-Threads - \item '\#pragma omp atomic' kritischer Abschnitt \item Zugriff auf gemeinsame Variable (z.B. Zähler) - \end{itemize} + \begin{itemize*} + \item '\#pragma omp single/master' Abschnitt wird nur durch einen/den Master-Thread ausgeführt + \item '\#pragma omp critical' kritischer Abschnitt + \item '\#pragma omp barrier' Warten auf alle Worker-Threads + \item '\#pragma omp atomic' kritischer Abschnitt \item Zugriff auf gemeinsame Variable (z.B. Zähler) + \end{itemize*} \item Speicherklauseln für Variablen - \begin{itemize} - \item 'shared' für alle Threads sichtbar/änderbar - \item 'private' jeder Thread hat eigene Kopie der Daten, wird nicht außerhalb initialisiert - \item 'reduction' private Daten, die am Ende des Abschnitts zu globalem Wert zusammengefasst werden - \item 'firstprivate / lastprivate' privat - initialisiert mit letztem Wert vor dem Abschnitt / Wert des letzten Threads der Iteration wird zurückgegeben - \end{itemize} + \begin{itemize*} + \item 'shared' für alle Threads sichtbar/änderbar + \item 'private' jeder Thread hat eigene Kopie der Daten, wird nicht außerhalb initialisiert + \item 'reduction' private Daten, die am Ende des Abschnitts zu globalem Wert zusammengefasst werden + \item 'firstprivate / lastprivate' privat - initialisiert mit letztem Wert vor dem Abschnitt / Wert des letzten Threads der Iteration wird zurückgegeben + \end{itemize*} \item zuweisung von Programmabschnitten zu Threads $\rightarrow$ statische Parallelität (geeignet für rekursive Abschnitte) - \begin{lstlisting}[ + \begin{lstlisting}[ language=C++, showspaces=false, basicstyle=\ttfamily @@ -5221,12 +5071,12 @@ std::lock(lk1, lk2); } \end{lstlisting} \item Task Programmierung - \begin{itemize} - \item reihum Threads zugewiesen werden - \item an beliebiger Stelle definiert werden können - \item von beliebigem Thread definiert werden kann - \end{itemize} - \begin{lstlisting}[ + \begin{itemize*} + \item reihum Threads zugewiesen werden + \item an beliebiger Stelle definiert werden können + \item von beliebigem Thread definiert werden kann + \end{itemize*} + \begin{lstlisting}[ language=C++, showspaces=false, basicstyle=\ttfamily @@ -5239,7 +5089,7 @@ std::lock(lk1, lk2); \#pragma omp taskwait return f1 + f2; \end{lstlisting} -\end{itemize} +\end{itemize*} \subsection{Parallele Programmierung in C++} @@ -5477,93 +5327,93 @@ countdown() -> \subsection{Parallele Programmierung in Java} Unterstützung durch -\begin{itemize} +\begin{itemize*} \item Thread-Konzept \item eingebaute Mechanismen zur Synchronisation nebenläufiger Prozesse \item spezielle High-Level-Klassen im Package - \newline \textbf{java.util.concurrent} -\end{itemize} + \newline \textbf{java.util.concurrent} +\end{itemize*} \subsubsection{Threads in Java} -\begin{itemize} +\begin{itemize*} \item Repräsentiert durch Klasse \textbf{java.lang.Thread} \item Implementierung eines eigenen Kontrollflusses - \begin{itemize} - \item Implementierung des Interface \textbf{java.lang.Runnable} - \begin{itemize} - \item keine weitere Beeinflussung des Threads über zusätzliche Methoden notwendig - \item soll von anderer Klasse als Thread abgeleitet werden - \end{itemize} - \item Subklasse von \textbf{java.lang.Thread} - \begin{itemize} - \item zusätzliche Methoden zur Steuerung des Ablaufs benötigt - \item keine andere Superklasse notwendig - \end{itemize} - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Implementierung des Interface \textbf{java.lang.Runnable} + \begin{itemize*} + \item keine weitere Beeinflussung des Threads über zusätzliche Methoden notwendig + \item soll von anderer Klasse als Thread abgeleitet werden + \end{itemize*} + \item Subklasse von \textbf{java.lang.Thread} + \begin{itemize*} + \item zusätzliche Methoden zur Steuerung des Ablaufs benötigt + \item keine andere Superklasse notwendig + \end{itemize*} + \end{itemize*} +\end{itemize*} \subsubsection{Threads: Runnable-Schnittstelle} Eigene Klasse muss \textbf{Runnable} implementieren -\begin{itemize} +\begin{itemize*} \item Methode \textbf{public void run()} - wird beim Start des Threads aufgerufen -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-099} \end{center} \subsubsection{Thread-Erzeugung} -\begin{itemize} +\begin{itemize*} \item Thread-Objekt mit Runnable-Objekt erzeugen \item Methode \textbf{start()} aufrufen - \begin{itemize} - \item Ruft \textbf{run()} auf - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Ruft \textbf{run()} auf + \end{itemize*} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-100} \end{center} \subsubsection{Threads: Subklasse von Thread} -\begin{itemize} +\begin{itemize*} \item Klasse muss von Thread abgeleitet werden \item Methode run() muss überschrieben werden -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-101} \end{center} \subsubsection{Thread-Erzeugung} -\begin{itemize} +\begin{itemize*} \item Objekt der eigenen Thread-Klasse erzeugen \item Methode \textbf{start()} aufrufen - \begin{itemize} - \item Ruft \textbf{run()} auf - \end{itemize} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-102} - \end{center} + \begin{itemize*} + \item Ruft \textbf{run()} auf + \end{itemize*} + \begin{center} + \centering + \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-102} + \end{center} \item Spätere Beeinflussung durch andere Threads möglich - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-103} - \end{center} -\end{itemize} + \begin{center} + \centering + \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-103} + \end{center} +\end{itemize*} \subsubsection{Threads: Wichtige Methoden} -\begin{itemize} +\begin{itemize*} \item \textbf{void start()} - \begin{itemize} - \item initiiert Ausführung des Threads durch Aufruf der Methode run - \end{itemize} + \begin{itemize*} + \item initiiert Ausführung des Threads durch Aufruf der Methode run + \end{itemize*} \item \textbf{void run()} - \begin{itemize} - \item die eigentliche Arbeitsmethode - \end{itemize} + \begin{itemize*} + \item die eigentliche Arbeitsmethode + \end{itemize*} \item \textbf{static void sleep(int millisec)} - \begin{itemize} - \item hält die Ausführung des aktuellen Threads für millisec Millisekunden an - \item Hat keinen Einfluss auf andere Threads! - \end{itemize} + \begin{itemize*} + \item hält die Ausführung des aktuellen Threads für millisec Millisekunden an + \item Hat keinen Einfluss auf andere Threads! + \end{itemize*} \item \textbf{void join()} - \begin{itemize} - \item blockiert den aufrufenden Thread so lange, bis der aufgerufene Thread beendet ist - \end{itemize} -\end{itemize} + \begin{itemize*} + \item blockiert den aufrufenden Thread so lange, bis der aufgerufene Thread beendet ist + \end{itemize*} +\end{itemize*} \subsubsection{Parallele Berechnung von Fibonacci-Zahlen} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-104} @@ -5574,126 +5424,126 @@ Thread-Erzeugung und Ausführung \end{center} \subsubsection{Wechselseitiger Ausschluss in Java} Schlüsselwort \textbf{synchronized} -\begin{itemize} +\begin{itemize*} \item Implementierung von sogenannten Monitoren bzw. locks (exklusiven Sperren) - \begin{itemize} - \item nur ein Thread darf den kritischen Abschnitt betreten - \item alle anderen Threads, die darauf zugrifen wollen, müssen auf Freigabe warten - \end{itemize} + \begin{itemize*} + \item nur ein Thread darf den kritischen Abschnitt betreten + \item alle anderen Threads, die darauf zugrifen wollen, müssen auf Freigabe warten + \end{itemize*} \item für Methoden: \textbf{public synchronized void doSomething()} - \begin{itemize} - \item nur ein Thread darf diese Methode auf einem Objekt zur gleichen Zeit ausführen - \end{itemize} + \begin{itemize*} + \item nur ein Thread darf diese Methode auf einem Objekt zur gleichen Zeit ausführen + \end{itemize*} \item für Anweisungen: \textbf{synchronized(anObject)\{…\}} - \begin{itemize} - \item nur ein Thread darf den Block betreten - \item Sperre wird durch das Objekt \textbf{anObject} verwaltet (jedem Java-Objekt ist eine Sperre zugeordnet) - \end{itemize} -\end{itemize} + \begin{itemize*} + \item nur ein Thread darf den Block betreten + \item Sperre wird durch das Objekt \textbf{anObject} verwaltet (jedem Java-Objekt ist eine Sperre zugeordnet) + \end{itemize*} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-106} \end{center} \subsubsection{wait \& notify} -\begin{itemize} +\begin{itemize*} \item Signalisierung zwischen Threads in Java \item Basismethoden der Klasse \textbf{java.lang.Object} \item \textbf{wait()}: der aktive Thread wartet an diesem Objekt, Sperren werden ggf. freigegeben. \item \textbf{notify()}: wekct an diesem Objekt wartenden Thread auf \item \textbf{notifyAll()}: weckt alle an diesem Objekt wartenden Threads auf \item \textbf{wait() \& notify()} dürfen nur in einem \textbf{synchronized}-Block aufgerufen werden -\end{itemize} +\end{itemize*} \subsubsection{Java: High-Level-Klassen} -\begin{itemize} +\begin{itemize*} \item Paket \textbf{java.util.concurrent} seit Java Version 1.5 \item Abstraktionsschicht versteckt Details über Thread-Erzeugung \item Übernimmt Erstellung und Überwachung von parallelen Tasks, u.a. - \begin{itemize} - \item \textbf{ExecutorService} zum erzeugen asynchroner Tasks - \item \textbf{Future}: Referenz auf diesen Task bzw. dessen Ergebnis - \item \textbf{ForkJoinPool \& RecursiveAction}: rekursives Aufteilen eines großen Problems - \end{itemize} -\end{itemize} + \begin{itemize*} + \item \textbf{ExecutorService} zum erzeugen asynchroner Tasks + \item \textbf{Future}: Referenz auf diesen Task bzw. dessen Ergebnis + \item \textbf{ForkJoinPool \& RecursiveAction}: rekursives Aufteilen eines großen Problems + \end{itemize*} +\end{itemize*} \subsubsection{Tasks und Futures in Java} -\begin{itemize} +\begin{itemize*} \item Task = logische Ausführungseinheit \item Thread = Mechanismus zur asynchronen/parallelen Ausführung von Tasks -\end{itemize} +\end{itemize*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-107} \end{center} \subsubsection{Future \& ExecutorService} -\begin{itemize} +\begin{itemize*} \item \textbf{ExecutorService} stellt Methoden zum Starten/Beenden/Steuern von parallelen Aufgaben bereit \item implementiert \textbf{Executor}-Interface - \begin{itemize} - \item definiert Methode \textbf{void execute(Runnable r)} - \end{itemize} + \begin{itemize*} + \item definiert Methode \textbf{void execute(Runnable r)} + \end{itemize*} \item Starten einer Aufgabe mit \textbf{submit} - \begin{itemize} - \item \textbf{Future submit(Callable c)} - \item \textbf{Future submit(Runnable r)} - \end{itemize} + \begin{itemize*} + \item \textbf{Future submit(Callable c)} + \item \textbf{Future submit(Runnable r)} + \end{itemize*} \item Zugriff auf das Ergebnis mit \textbf{get} - \begin{itemize} - \item \textbf{T get(long timeout, TimeUnit unit)} - \item \textbf{T get()} - \end{itemize} -\end{itemize} + \begin{itemize*} + \item \textbf{T get(long timeout, TimeUnit unit)} + \item \textbf{T get()} + \end{itemize*} +\end{itemize*} \subsubsection{Future \& ExecutorService: Beispiel} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-108} \end{center} \subsubsection{RecursiveAction \& Fork/Join} -\begin{itemize} +\begin{itemize*} \item Rekursives Zerlegen eines großen Problems in kleinere Probleme \item Solange bis Problem klein genug um direkt ausgeführt werden zu können \item Task erstellt zwei oder mehr Teiltasks von sich selbst $\rightarrowtail$ Datenparallelität \item \textbf{ForkJoinPool} zum Ausführen - \begin{itemize} - \item implementiert \textbf{Executor} Interface - \end{itemize} -\end{itemize} + \begin{itemize*} + \item implementiert \textbf{Executor} Interface + \end{itemize*} +\end{itemize*} \subsubsection{Fork/Join: Beispiel} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-109} \end{center} Starten der Verarbeitung: -\begin{enumerate} +\begin{enumerate*} \item (große) Gesamtaufgabe erstellen \item ForkJoinPool erstellen \item Aufgabe vom Pool ausführen lassen -\end{enumerate} +\end{enumerate*} \begin{center} \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-110} \end{center} \subsubsection{Fazit} -\begin{itemize} +\begin{itemize*} \item Parallelprogrammierung in Java sehr ähnlich zu C++ \item Konzepte: Threads, kritische Abschnitte über \textbf{synchronized} \item mächtige Abstraktionen in \textbf{java.util.concurrent} - \begin{itemize} - \item Tasks und Futures, Executor und Threadpool - \item thread-sichere Datenstrukturen - \item Synchronisation: Barrieren, Semaphoren,… - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Tasks und Futures, Executor und Threadpool + \item thread-sichere Datenstrukturen + \item Synchronisation: Barrieren, Semaphoren,… + \end{itemize*} +\end{itemize*} \subsection{Parallele Programmierung in Java} -\begin{itemize} +\begin{itemize*} \item Unterstützung durch: - \begin{itemize} - \item Thread-Konzept - \item eingebaute Mechanismen zur Synchronisation nebenläufiger Prozesse - \item spezielle High-Level-Klassen im Package 'java.util.concurrent' - \end{itemize} + \begin{itemize*} + \item Thread-Konzept + \item eingebaute Mechanismen zur Synchronisation nebenläufiger Prozesse + \item spezielle High-Level-Klassen im Package 'java.util.concurrent' + \end{itemize*} \item Threads in Java - \begin{itemize} - \item Repräsentiert durch Klasse java.lang.Thread - \item Implementierung eines eigenen Kontrollflusses - \item Eigene Klasse muss Runnable implementieren - \end{itemize} - \begin{lstlisting}[ + \begin{itemize*} + \item Repräsentiert durch Klasse java.lang.Thread + \item Implementierung eines eigenen Kontrollflusses + \item Eigene Klasse muss Runnable implementieren + \end{itemize*} + \begin{lstlisting}[ language=java, showspaces=false, basicstyle=\ttfamily @@ -5716,56 +5566,56 @@ Starten der Verarbeitung: } \end{lstlisting} \item Threads: Wichtige Methoden - \begin{itemize} - \item void start(): initiiert Ausführung des Threads durch Aufruf der Methode run - \item void run(): die eigentliche Arbeitsmethode - \item static void sleep(int millis): hält die Ausführung des aktuellen Threads für 'millis' Millisekunden an; Keinen Einfluss auf andere Threads! - \item void join(): blockiert den aufrufenden Thread so lange, bis der aufgerufene Thread beendet ist - \end{itemize} + \begin{itemize*} + \item void start(): initiiert Ausführung des Threads durch Aufruf der Methode run + \item void run(): die eigentliche Arbeitsmethode + \item static void sleep(int millis): hält die Ausführung des aktuellen Threads für 'millis' Millisekunden an; Keinen Einfluss auf andere Threads! + \item void join(): blockiert den aufrufenden Thread so lange, bis der aufgerufene Thread beendet ist + \end{itemize*} \item Wechselseitiger Ausschluss in Java - \begin{itemize} - \item Schlüsselwort synchronized - \begin{itemize} - \item Implementierung von sogenannten Monitoren bzw. locks (exklusiven Sperren); nur ein Thread darf den kritischen Abschnitt betreten; alle anderen Threads, die darauf zugreifen wollen, müssen auf Freigabe warten - \item für Methoden: - \begin{lstlisting} + \begin{itemize*} + \item Schlüsselwort synchronized + \begin{itemize*} + \item Implementierung von sogenannten Monitoren bzw. locks (exklusiven Sperren); nur ein Thread darf den kritischen Abschnitt betreten; alle anderen Threads, die darauf zugreifen wollen, müssen auf Freigabe warten + \item für Methoden: + \begin{lstlisting} public synchronized void doSomething() \end{lstlisting} - \begin{itemize} - \item nur ein Thread darf diese Methode auf einem Objekt zur gleichen Zeit ausführen - \end{itemize} - \item für Anweisungen: synchronized(anObject) { ... } - \begin{itemize} - \item nur ein Thread darf den Block betreten - \item Sperre wird durch das Objekt anObject verwaltet (jedem Java-Objekt ist ein Sperre zugeordnet) - \end{itemize} - \end{itemize} - \item wait \& notify - \begin{itemize} - \item Signalisierung zwischen Threads in Java - \item Basismethoden der Klasse java.lang.Object - \item wait() : der aktive Thread wartet an diesem Objekt, Sperren werden ggf. freigegeben! - \item notify() : weckt an diesem Objekt wartenden Thread auf - \item notifyAll() : weckt alle an diesem Objekt wartenden Threads auf - \item wait() \& notify() dürfen nur in einem synchronized -Block aufgerufen werden - \end{itemize} - \item Java: High-Level-Klassen - \begin{itemize} - \item Paket java.util.concurrent seit Java Version 1.5 - \item Abstraktionsschicht versteckt Details über Thread-Erzeugung - \item Übernimmt Erstellung und Überwachung von parallelen Tasks, u.a. - \begin{itemize} - \item ExecutorService zum Erzeugen asynchroner Tasks - \item Future: Referenz auf diesen Task bzw. dessen Ergebnis - \item ForkJoinPool \& RecursiveAction: rekursives - \item Aufteilen eines großen Problems - \end{itemize} - \end{itemize} - \item Tasks und Futures in Java - \begin{itemize} - \item Task = logische Ausführungseinheit - \item Thread = Mechanismus zur asynchronen/parallelen Ausführung von Tasks - \begin{lstlisting}[ + \begin{itemize*} + \item nur ein Thread darf diese Methode auf einem Objekt zur gleichen Zeit ausführen + \end{itemize*} + \item für Anweisungen: synchronized(anObject) { ... } + \begin{itemize*} + \item nur ein Thread darf den Block betreten + \item Sperre wird durch das Objekt anObject verwaltet (jedem Java-Objekt ist ein Sperre zugeordnet) + \end{itemize*} + \end{itemize*} + \item wait \& notify + \begin{itemize*} + \item Signalisierung zwischen Threads in Java + \item Basismethoden der Klasse java.lang.Object + \item wait() : der aktive Thread wartet an diesem Objekt, Sperren werden ggf. freigegeben! + \item notify() : weckt an diesem Objekt wartenden Thread auf + \item notifyAll() : weckt alle an diesem Objekt wartenden Threads auf + \item wait() \& notify() dürfen nur in einem synchronized -Block aufgerufen werden + \end{itemize*} + \item Java: High-Level-Klassen + \begin{itemize*} + \item Paket java.util.concurrent seit Java Version 1.5 + \item Abstraktionsschicht versteckt Details über Thread-Erzeugung + \item Übernimmt Erstellung und Überwachung von parallelen Tasks, u.a. + \begin{itemize*} + \item ExecutorService zum Erzeugen asynchroner Tasks + \item Future: Referenz auf diesen Task bzw. dessen Ergebnis + \item ForkJoinPool \& RecursiveAction: rekursives + \item Aufteilen eines großen Problems + \end{itemize*} + \end{itemize*} + \item Tasks und Futures in Java + \begin{itemize*} + \item Task = logische Ausführungseinheit + \item Thread = Mechanismus zur asynchronen/parallelen Ausführung von Tasks + \begin{lstlisting}[ language=java, showspaces=false, basicstyle=\ttfamily @@ -5778,48 +5628,48 @@ Starten der Verarbeitung: Thread thread = new Thread(task); thread.start(); \end{lstlisting} - \end{itemize} - \item Future \& ExecutorService - \begin{itemize} - \item ExecutorService stellt Methoden zum Starten/Beenden/Steuern von parallelen Aufgaben bereit - \item implementiert Executor -Interface - \begin{itemize} - \item definiert Methode void execute(Runnable r) - \end{itemize} - \item Starten einerAufgabe mit submit - \begin{itemize} - \item Future submit(Callable c) - \item Future submit(Runnable r) - \end{itemize} - \item Zugriff auf das Ergebnis mit get - \begin{itemize} - \item T get(long timeout, TimeUnit unit) - \item T get() - \end{itemize} - \end{itemize} - \item RecursiveAction \& Fork/Join - \begin{itemize} - \item Rekursives Zerlegen eines großen Problems in kleinere Probleme - \item Solange bis Problem klein genug um direkt ausgeführt werden zu können - \item Task erstellt zwei oder mehr Teiltasks von sich selbst $\rightarrow$ Datenparallelität - \item ForkJoinPool zum Ausführen $\rightarrow$ implementiert Executor Interface - \end{itemize} - \item Fazit - \begin{itemize} - \item Parallelprogrammierung in Java sehr ähnlich zu C++ - \item Konzepte: Threads, kritische Abschnitte über synchronized - \item mächtige Abstraktionen in java.util.concurrent - \begin{itemize} - \item Tasks und Futures, Executor und ThreadPool - \item thread-sichere Datenstrukturen - \item Synchronisation: Barrieren, Semaphoren, ... - \end{itemize} - \end{itemize} - \end{itemize} -\end{itemize} + \end{itemize*} + \item Future \& ExecutorService + \begin{itemize*} + \item ExecutorService stellt Methoden zum Starten/Beenden/Steuern von parallelen Aufgaben bereit + \item implementiert Executor -Interface + \begin{itemize*} + \item definiert Methode void execute(Runnable r) + \end{itemize*} + \item Starten einerAufgabe mit submit + \begin{itemize*} + \item Future submit(Callable c) + \item Future submit(Runnable r) + \end{itemize*} + \item Zugriff auf das Ergebnis mit get + \begin{itemize*} + \item T get(long timeout, TimeUnit unit) + \item T get() + \end{itemize*} + \end{itemize*} + \item RecursiveAction \& Fork/Join + \begin{itemize*} + \item Rekursives Zerlegen eines großen Problems in kleinere Probleme + \item Solange bis Problem klein genug um direkt ausgeführt werden zu können + \item Task erstellt zwei oder mehr Teiltasks von sich selbst $\rightarrow$ Datenparallelität + \item ForkJoinPool zum Ausführen $\rightarrow$ implementiert Executor Interface + \end{itemize*} + \item Fazit + \begin{itemize*} + \item Parallelprogrammierung in Java sehr ähnlich zu C++ + \item Konzepte: Threads, kritische Abschnitte über synchronized + \item mächtige Abstraktionen in java.util.concurrent + \begin{itemize*} + \item Tasks und Futures, Executor und ThreadPool + \item thread-sichere Datenstrukturen + \item Synchronisation: Barrieren, Semaphoren, ... + \end{itemize*} + \end{itemize*} + \end{itemize*} +\end{itemize*} \subsection{Zusammenfassung} -\begin{itemize} +\begin{itemize*} \item Parallelprogrammierung als wichtige Technik zur Nutzung moderner Hardware (Multicore, GPU, …) \item verschiedene Architekturen und Programmiermodelle \item Instruktions-, Daten- und Taskparallelität @@ -5828,302 +5678,302 @@ Starten der Verarbeitung: \item hoher Abstraktionsgrad funktionaler Sprachen \item C++/Java: Thread-Modell und Synchronisation mit vielen weiteren Konzepten \item höherwertige Abstraktion durch zusätzliche Bibliotheken und Programmierschnittstellen -\end{itemize} +\end{itemize*} \section{Verteilte Programmierung} \subsection{Grundlagen} -\begin{itemize} +\begin{itemize*} \item mehrere Rechner \item Prozesse auf verschiedenen Rechnern \item Kommunikation über Knotengrenzen hinweg \item Behandlung von Knoten- oder Netzwerkausfällen -\end{itemize} +\end{itemize*} -\begin{itemize} +\begin{itemize*} \item viele verschiedene Systeme (Knoten) zur Verfügung - \begin{itemize} - \item PC, Server, virtuelle Maschine - \item Lastverteilung, Spezialisierung auf bestimmte Probleme, ... - \end{itemize} + \begin{itemize*} + \item PC, Server, virtuelle Maschine + \item Lastverteilung, Spezialisierung auf bestimmte Probleme, ... + \end{itemize*} \item Knoten sind über Netwerke verbunden - \begin{itemize} - \item LAN (Wohnräume, Büros, Uni-Campus): bis zu 10 GBit/s - \item Metropolitan Area Network (MAN; dichtbesiedelte Regionen, Behördennetze): bis zu 10 GBit/s - \item Wide Area Network (WAN; weltweite Vernetzung): hohe Kapazitäten zwischen ISPs - \end{itemize} -\end{itemize} + \begin{itemize*} + \item LAN (Wohnräume, Büros, Uni-Campus): bis zu 10 GBit/s + \item Metropolitan Area Network (MAN; dichtbesiedelte Regionen, Behördennetze): bis zu 10 GBit/s + \item Wide Area Network (WAN; weltweite Vernetzung): hohe Kapazitäten zwischen ISPs + \end{itemize*} +\end{itemize*} Wofür werden verteilte Systeme eingesetzt? -\begin{itemize} +\begin{itemize*} \item Gemeinsame Nutzung von Ressourcen - \begin{itemize} - \item Cloud-Umgebungen - \item verteilte (Datenbank)-Systeme - \end{itemize} + \begin{itemize*} + \item Cloud-Umgebungen + \item verteilte (Datenbank)-Systeme + \end{itemize*} \item Teilaufgaben in großen Anwendungen - \begin{itemize} - \item parallele Ausführung - \item getrennte Teilaufgaben (Micro-Services) - \end{itemize} + \begin{itemize*} + \item parallele Ausführung + \item getrennte Teilaufgaben (Micro-Services) + \end{itemize*} \item Informationsaustausch - \begin{itemize} - \item E-Mail, Messenger - \item verteilte Algorithmen - \end{itemize} -\end{itemize} + \begin{itemize*} + \item E-Mail, Messenger + \item verteilte Algorithmen + \end{itemize*} +\end{itemize*} Software-Architekturen -\begin{itemize} +\begin{itemize*} \item Früher: Hardware, Betriebssystem, Anwendung - \begin{itemize} - \item Virtualisierung von Prozessor, Speicher, E/A Systemen - \item Interprozesskommunikation (IPC) - \end{itemize} + \begin{itemize*} + \item Virtualisierung von Prozessor, Speicher, E/A Systemen + \item Interprozesskommunikation (IPC) + \end{itemize*} \item Middlewaresysteme: HW, OS, Middleware, Anwendung - \begin{itemize} - \item verteilte Dienste - \item Programmierparadigmen: RPC, Client/Server, ... - \end{itemize} + \begin{itemize*} + \item verteilte Dienste + \item Programmierparadigmen: RPC, Client/Server, ... + \end{itemize*} \item Heute: Virtualisierung - \begin{itemize} - \item VM Hypervisor: verstecken Hardware vor Betriebssystem - \item Docker: eine Anwendung pro Container - \end{itemize} -\end{itemize} + \begin{itemize*} + \item VM Hypervisor: verstecken Hardware vor Betriebssystem + \item Docker: eine Anwendung pro Container + \end{itemize*} +\end{itemize*} Herausforderungen: -\begin{itemize} +\begin{itemize*} \item viele verschiedene Computer/Server - \begin{itemize} - \item verschiedene Betriebssysteme - \item unterschiedliche Leistungsfähigkeit - \end{itemize} + \begin{itemize*} + \item verschiedene Betriebssysteme + \item unterschiedliche Leistungsfähigkeit + \end{itemize*} \item Systemkomponenten müssen miteinander kommunizieren \item verteilte Algorithmen: Nachrichten senden, empfangen, bestätigen, Synchronisation \item Knotenausfälle behandeln -\end{itemize} +\end{itemize*} $\Rightarrow$ brauchen Modelle zur Beschreibung der Kommunikation Anforderungen an den Betrieb eines (großen) verteilten Systems -\begin{itemize} +\begin{itemize*} \item (Last-)Skalierbarkeit (Scale-out) - \begin{itemize} - \item viele kleine Server - statt eines großen - \item neue Server nach Bedarf hinzufügen - \end{itemize} + \begin{itemize*} + \item viele kleine Server - statt eines großen + \item neue Server nach Bedarf hinzufügen + \end{itemize*} \item Funktionssicherheit (Safety) / IT-Sicherheit (Security) \item Fehlertoleranz / Verfügbarkeit - \begin{itemize} - \item Ausfälle von einzelnen Knoten kompensieren - \item Redundante Verarbeitung - \end{itemize} + \begin{itemize*} + \item Ausfälle von einzelnen Knoten kompensieren + \item Redundante Verarbeitung + \end{itemize*} \item Offenheit / Interoperabilität - \begin{itemize} - \item neue Knoten und Systeme einfach integrieren - \end{itemize} + \begin{itemize*} + \item neue Knoten und Systeme einfach integrieren + \end{itemize*} \item Transparenz - \begin{itemize} - \item verstecke die vielen Server vor Anwendern - \end{itemize} -\end{itemize} + \begin{itemize*} + \item verstecke die vielen Server vor Anwendern + \end{itemize*} +\end{itemize*} \subsection{Grundlagen verteilter Programmierung in Java und Erlang} \paragraph{Sockets} -\begin{itemize} +\begin{itemize*} \item Verteilte Programmierung: Wir müssen einen entfernten Computer ansprechen \item benötigen: Adresse $\rightarrow$ IP-Adresse \item da mehrere Dienste auf demselben Computer laufen lauscht jeder Dienst auf einem Port (Nummer) - \begin{itemize} - \item Wichtige Ports: 80 WWW, 20 (FTP), 25 (SMTP) - \end{itemize} + \begin{itemize*} + \item Wichtige Ports: 80 WWW, 20 (FTP), 25 (SMTP) + \end{itemize*} \item Socket beschreibt einen Endpunkt, d.h. Adresse \& Port in einem TCP (oder UDP) Netzwerk \item Server-Socket wartet auf Verbindungen \item Client initiiert Verbindung, ebenfalls über einen (Client-)Socket -\end{itemize} +\end{itemize*} Socket in dem package ''java.net.Socket'' -\begin{itemize} +\begin{itemize*} \item einen ServerSocket auf Port 4242 erstellen - \begin{lstlisting} + \begin{lstlisting} ServerSocket serverSocket = new ServerSocket(4242); \end{lstlisting} \item Warte blockierend auf Verbindungen - \begin{lstlisting} + \begin{lstlisting} Socket client = serverSocket.accept(); \end{lstlisting} \item Client Socket ertellen und zum Server verbinden - \begin{lstlisting} + \begin{lstlisting} Socket client = new Socket("localhost", 4242); \end{lstlisting} \item Sockets in ähnlicher Form in C/C++ -\end{itemize} +\end{itemize*} \paragraph{Aktormodell} -\begin{itemize} +\begin{itemize*} \item formales Modell für Nebenläufigkeit und Verteilung \item Basis für verschiedene Programmiersprachen/Frameworks: Erlang, Akka (Scala/Java) \item Prinzipien: - \begin{itemize} - \item Aktor kapselt Zustand und Verhalten - \item Aktoren sind aktiv - \item Aktoren kommunizieren durch Nachrichtenaustausch - \begin{itemize} - \item Nichtblockierendes Senden - \item Blockierendes Empfangen - \end{itemize} - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Aktor kapselt Zustand und Verhalten + \item Aktoren sind aktiv + \item Aktoren kommunizieren durch Nachrichtenaustausch + \begin{itemize*} + \item Nichtblockierendes Senden + \item Blockierendes Empfangen + \end{itemize*} + \end{itemize*} +\end{itemize*} Aktormodell in Erlang -\begin{itemize} +\begin{itemize*} \item Aktormodell in Erlang nativ umgesetzt - \begin{itemize} - \item Sende- und Empfangsoperationen schon für parallele Programmierung benutzt - \item bisher aber nur auf einem Knoten - \end{itemize} + \begin{itemize*} + \item Sende- und Empfangsoperationen schon für parallele Programmierung benutzt + \item bisher aber nur auf einem Knoten + \end{itemize*} \item Programmbestandteile im Aktormodell - \begin{itemize} - \item Verhaltensdefinition $\Rightarrow$ f() $\rightarrow$ ... end. - \item Erzeugen neuer Aktoren $\Rightarrow$ Pid = spawn(fun ... ) . - \item Empfangen von Nachrichten $\Rightarrow$ receive ... end . - \item Senden $\Rightarrow$ Pid ! Request . - \end{itemize} + \begin{itemize*} + \item Verhaltensdefinition $\Rightarrow$ f() $\rightarrow$ ... end. + \item Erzeugen neuer Aktoren $\Rightarrow$ Pid = spawn(fun ... ) . + \item Empfangen von Nachrichten $\Rightarrow$ receive ... end . + \item Senden $\Rightarrow$ Pid ! Request . + \end{itemize*} \item kein globaler Zustand -\end{itemize} +\end{itemize*} Cookie-System -\begin{itemize} +\begin{itemize*} \item erteilte Erlang-Knoten benötigen zur Kommunikation gemeinsames Magic Cookie (Passwort) \item Mehrere Varianten - \begin{itemize} - \item Datei \~/.erlang.cookie - \item Erlang-Funktion - \begin{lstlisting}[ + \begin{itemize*} + \item Datei \~/.erlang.cookie + \item Erlang-Funktion + \begin{lstlisting}[ language=erlang, showspaces=false, basicstyle=\ttfamily ]:set_cookie(node(), Cookie). \end{lstlisting} - \item Option - \begin{lstlisting} + \item Option + \begin{lstlisting} erl -setcookie Cookie \end{lstlisting} - \end{itemize} + \end{itemize*} \item Verbindungsaufbau mittels Funktion - \begin{lstlisting} + \begin{lstlisting} net_adm:ping(adress) \end{lstlisting} -\end{itemize} +\end{itemize*} Das Alternating Bit Protokoll - Übersicht -\begin{itemize} +\begin{itemize*} \item ermöglicht es, Nachrichten über einen verlustbehafteten Kommunikationskanal vollständig zu übertragen, sofern Verluste nur gelegentlich auftreten (transiente Fehler) \item Empfänger quittiert jedes erhaltene Paket (Ackknowledgement, kurz: Ack) \item Achtung: Kanal kann Nachrichten und Acks verlieren - \begin{itemize} - \item $\Rightarrow$ benötigen je zwei unterschiedliche Sequenznummern und Acks - \end{itemize} + \begin{itemize*} + \item $\Rightarrow$ benötigen je zwei unterschiedliche Sequenznummern und Acks + \end{itemize*} \item Empfänger liefert eine Nachricht nur beim ersten Empfang aus (keine Duplikate) \item bei Timeout: Nachricht erneut senden \item bei Erhalt eines unerwarteten Ack: aktuelle Nachricht erneut senden -\end{itemize} +\end{itemize*} \subsection{Kommunikationsmodelle \& Implementierungen} das Verhalten der Teilnehmer ist in Kommunikationsmodellen beschrieben Es gibt viele verschiedene Modelle, z.B. für Botschaftenbasierte Modelle -\begin{itemize} +\begin{itemize*} \item Auftragsorientierte Modelle \item Funktionsaufrufbasierte Modelle \item Blackboards (Tuple Spaces) \item Ereignisbasierte Modelle \item (Strombasierte Modelle) \item (Wissenbasierte Modelle) -\end{itemize} +\end{itemize*} Kommunikationspartner sind für uns: -\begin{itemize} +\begin{itemize*} \item Threads/Prozesse innerhalb der verteilten Anwendung \item Komponenten verteilter Systeme (Browser $\leftrightarrow$ Web-Server, DB Client $\leftrightarrow$ DB-Server) -\end{itemize} +\end{itemize*} Modellbestandteile -\begin{itemize} +\begin{itemize*} \item Rollenmodell - \begin{itemize} - \item gemeinsames Handlungsmuster festlegen - \item z.B.: Anrufer/Angerufener, Client/Server, Quelle/Senke - \end{itemize} + \begin{itemize*} + \item gemeinsames Handlungsmuster festlegen + \item z.B.: Anrufer/Angerufener, Client/Server, Quelle/Senke + \end{itemize*} \item Datenmodell - \begin{itemize} - \item einheitliche Interpretation der ausgetauschten Daten - \item z.B.: Dateiformate (XML/JSON), Kodierungen (MPEG4/H.264) - \end{itemize} + \begin{itemize*} + \item einheitliche Interpretation der ausgetauschten Daten + \item z.B.: Dateiformate (XML/JSON), Kodierungen (MPEG4/H.264) + \end{itemize*} \item Fehlersemantiken - \begin{itemize} - \item Einvernehmen über Wirkung von Ausfällen - \item Eigenschaften von Kommunikationsoperationen müssen bei Ausfällen garantiert werden - \end{itemize} + \begin{itemize*} + \item Einvernehmen über Wirkung von Ausfällen + \item Eigenschaften von Kommunikationsoperationen müssen bei Ausfällen garantiert werden + \end{itemize*} \item Terminierungssemantik - \begin{itemize} - \item Einvernehmen über Ende der Kommunikation - \item Garantien über Ende von Kommunikationsoperationen (auch bei Ausfällen) - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Einvernehmen über Ende der Kommunikation + \item Garantien über Ende von Kommunikationsoperationen (auch bei Ausfällen) + \end{itemize*} +\end{itemize*} Kommunikationsarten -\begin{itemize} +\begin{itemize*} \item Wann ist eine Kommunikationsoperation abgeschlossen? \item entspricht Terminierungssemantik \item zwei grundlegende Arten: - \begin{itemize} - \item synchron: - \begin{itemize} - \item blockierend - \item Teilnehmer wartet bis Gegenseite bereit - \item kann lange dauern, Sender kann nicht weiter arbeiten - \item Senden: Botschaftenankunft garantiert, einfache Implementierung synchroner Aktivitäten - \item Empfangen: Botschaftenankunft einfach und präzise feststellbar - \end{itemize} - \item asynchron: - \begin{itemize} - \item nicht-blockierend - \item Teilnehmer wartet nicht auf Gegenseite (”fire and forget”) - \item unklar ob Botschaft angekommen - \item Senden: einfache Implementierung von Nebenläufigkeit - \item Empfangen: unklar wann Botschaft ankommt, einfache Implementierung von Nebenläufigkeit - \end{itemize} - \item für Senden und Empfangen - \end{itemize} -\end{itemize} + \begin{itemize*} + \item synchron: + \begin{itemize*} + \item blockierend + \item Teilnehmer wartet bis Gegenseite bereit + \item kann lange dauern, Sender kann nicht weiter arbeiten + \item Senden: Botschaftenankunft garantiert, einfache Implementierung synchroner Aktivitäten + \item Empfangen: Botschaftenankunft einfach und präzise feststellbar + \end{itemize*} + \item asynchron: + \begin{itemize*} + \item nicht-blockierend + \item Teilnehmer wartet nicht auf Gegenseite (”fire and forget”) + \item unklar ob Botschaft angekommen + \item Senden: einfache Implementierung von Nebenläufigkeit + \item Empfangen: unklar wann Botschaft ankommt, einfache Implementierung von Nebenläufigkeit + \end{itemize*} + \item für Senden und Empfangen + \end{itemize*} +\end{itemize*} Fehlerbehandlung -\begin{itemize} +\begin{itemize*} \item unverlässliches vs. verlässliches Senden - \begin{itemize} - \item "Brief vs. Einschreiben” - \end{itemize} + \begin{itemize*} + \item "Brief vs. Einschreiben” + \end{itemize*} \item verlässliche Kommunikation erfordert - \begin{itemize} - \item Quittierung (Acknowledgements) $\rightarrow$ mehr Daten senden - \item Timeouts $\rightarrow$ Zeitverwaltung, langes Warten - \end{itemize} + \begin{itemize*} + \item Quittierung (Acknowledgements) $\rightarrow$ mehr Daten senden + \item Timeouts $\rightarrow$ Zeitverwaltung, langes Warten + \end{itemize*} \item vielfältige Fehlermöglichkeiten in verteilten Anwendungen: - \begin{itemize} - \item Kommunikations-/Netzwerkfehler: $\rightarrow$ Nachricht/Antwort gar nicht oder verzögert zugestellt - \item Serverausfall: Nachricht empfangen? Operation ausgeführt? - \item Clientausfall: Aufruf gültig? Bestätigung erhalten? - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Kommunikations-/Netzwerkfehler: $\rightarrow$ Nachricht/Antwort gar nicht oder verzögert zugestellt + \item Serverausfall: Nachricht empfangen? Operation ausgeführt? + \item Clientausfall: Aufruf gültig? Bestätigung erhalten? + \end{itemize*} +\end{itemize*} Fehlerbehandlung in Erlang -\begin{itemize} +\begin{itemize*} \item Timeout beim Warten auf Nachrichten \item Wenn keine passende Nachricht innerhalb Time msecs. empfangen wird, dann Rückgabewert des after-Ausdrucks -\end{itemize} +\end{itemize*} \begin{lstlisting}[ language=erlang, showspaces=false, @@ -6137,149 +5987,149 @@ end \end{lstlisting} Überwachung von Erlang Prozessen -\begin{itemize} +\begin{itemize*} \item Linking von Prozessen: link(Pid). \item M überwacht S; S bricht durch Fehler ab \item M wartet auf EXIT Nachricht von S $\rightarrow$ asynchroner Handler nötig -\end{itemize} +\end{itemize*} Umgang mit Fehlern (Timeouts, Ausfälle): -\begin{itemize} +\begin{itemize*} \item Maybe: - \begin{itemize} - \item keine Wiederholung - \item keine Ausführungsgarantie - \end{itemize} + \begin{itemize*} + \item keine Wiederholung + \item keine Ausführungsgarantie + \end{itemize*} \item At-least-once: - \begin{itemize} - \item wiederholte Ausführung, aber keine Erkennung von Nachrichtduplikaten - \item nur für idempotente Operationen (Lesen) - \end{itemize} + \begin{itemize*} + \item wiederholte Ausführung, aber keine Erkennung von Nachrichtduplikaten + \item nur für idempotente Operationen (Lesen) + \end{itemize*} \item At-most-once: - \begin{itemize} - \item garantiert, dass mehrfache Aufrufe nur zu einziger Ausführung führen - \item z.B. durch Sequenznummern (erfordert Protokollierung zur Duplikateliminierung) - \item für nicht-idempotente Operationen (schreibend, z.B. Einfügen, Löschen) - \end{itemize} -\end{itemize} + \begin{itemize*} + \item garantiert, dass mehrfache Aufrufe nur zu einziger Ausführung führen + \item z.B. durch Sequenznummern (erfordert Protokollierung zur Duplikateliminierung) + \item für nicht-idempotente Operationen (schreibend, z.B. Einfügen, Löschen) + \end{itemize*} +\end{itemize*} \subsection{Grundlagen} \subsubsection{Lernziele} -\begin{itemize} +\begin{itemize*} \item Verständnis von Techniken verteilter Programmierung als Paradigma - \begin{itemize} - \item Modelle und Konzepte unabhängig von Programmiersprache und Betriebssystem - \item Herausforderungen und Besonderheiten verteilter Programme - \end{itemize} + \begin{itemize*} + \item Modelle und Konzepte unabhängig von Programmiersprache und Betriebssystem + \item Herausforderungen und Besonderheiten verteilter Programme + \end{itemize*} \item Kennenlernen konkreter Konzepte und Mechanismen - \begin{itemize} - \item praktische Beispiele in Java, Erlang und C++ - \item Bewertung und Vergleich verschiedener Plattformen - \end{itemize} -\end{itemize} + \begin{itemize*} + \item praktische Beispiele in Java, Erlang und C++ + \item Bewertung und Vergleich verschiedener Plattformen + \end{itemize*} +\end{itemize*} \subsubsection{Einordnung} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-001} \end{center} \subsubsection{Ziele} -\begin{itemize} +\begin{itemize*} \item Bisher: - \begin{itemize} - \item eine Maschine - \item Prozesse kommunizieren nur innerhalb dieser Maschine (shared Memory vs. Message Passing) - - \end{itemize} + \begin{itemize*} + \item eine Maschine + \item Prozesse kommunizieren nur innerhalb dieser Maschine (shared Memory vs. Message Passing) + + \end{itemize*} \item Jetzt: - \begin{itemize} - \item mehrere Rechner - \item Prozesse auf verschiedenen Rechnern - \end{itemize} + \begin{itemize*} + \item mehrere Rechner + \item Prozesse auf verschiedenen Rechnern + \end{itemize*} \item Erfordert: - \begin{itemize} - \item Kommunikation über Knotengrenzen hinweg - \item Behandlung von Knoten- oder Netzwerkausfällen - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Kommunikation über Knotengrenzen hinweg + \item Behandlung von Knoten- oder Netzwerkausfällen + \end{itemize*} +\end{itemize*} \subsubsection{Motivation} -\begin{itemize} +\begin{itemize*} \item viele verschiedene Systeme (Knoten) zur Verfügung - \begin{itemize} - \item PC, Server, virtuelle Maschinen - \item Lastverteilung, Spezialisierung auf bestimmte Probleme - \end{itemize} + \begin{itemize*} + \item PC, Server, virtuelle Maschinen + \item Lastverteilung, Spezialisierung auf bestimmte Probleme + \end{itemize*} \item Knoten sind über Netzwerke verbunden - \begin{itemize} - \item LAN(Wohnräume, Büros,…): bis zu 10 Gbit/s - \item MAN(Metropolitan Area Network, Behördennetze, dicht besiedelte Regionen): bis zu 10 Gbit/s - \item WAN(Wide Area Network, weltweite Vernetzung): hohe Kapazitäten zwischen den ISPs - \end{itemize} -\end{itemize} + \begin{itemize*} + \item LAN(Wohnräume, Büros,…): bis zu 10 Gbit/s + \item MAN(Metropolitan Area Network, Behördennetze, dicht besiedelte Regionen): bis zu 10 Gbit/s + \item WAN(Wide Area Network, weltweite Vernetzung): hohe Kapazitäten zwischen den ISPs + \end{itemize*} +\end{itemize*} Wofür werden verteilte Systeme eingesetzt? -\begin{itemize} +\begin{itemize*} \item Gemeinsame Nutzung von Ressourcen - \begin{itemize} - \item Cloud-Umgebungen - \item verteilte Datenbanksysteme - - \end{itemize} + \begin{itemize*} + \item Cloud-Umgebungen + \item verteilte Datenbanksysteme + + \end{itemize*} \item Teilaufgaben in großen Anwendungen - \begin{itemize} - \item parallele Ausführung - \item getrennte Teilaufgaben (Micro-Services) - - \end{itemize} + \begin{itemize*} + \item parallele Ausführung + \item getrennte Teilaufgaben (Micro-Services) + + \end{itemize*} \item Informationsaustausch - \begin{itemize} - \item Email, Messenger - \item verteilte Algorithmen - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Email, Messenger + \item verteilte Algorithmen + \end{itemize*} +\end{itemize*} \subsubsection{Architekturen} \color{orange} Software-Architekturen \color{black} -\begin{enumerate} +\begin{enumerate*} \item Früher: Hardware, Betriebssystem, Anwendung - \begin{itemize} - \item Virtualisierung von Prozessor, Speicher, E/A Systemen - \item Interprozesskommunikation - \end{itemize} + \begin{itemize*} + \item Virtualisierung von Prozessor, Speicher, E/A Systemen + \item Interprozesskommunikation + \end{itemize*} \item Middlewaresysteme: Hardware, OS, Middleware, Anwendung - \begin{itemize} - \item verteilte Dienste - \item Programmierparadigmen: RPC, Client/Server,… - \item Java, CORBA, … - \end{itemize} + \begin{itemize*} + \item verteilte Dienste + \item Programmierparadigmen: RPC, Client/Server,… + \item Java, CORBA, … + \end{itemize*} \item Heute: Virtualisierung - \begin{itemize} - \item VM Hypervisor: verstecken Hardware vor dem Betriebssystem - \item Docker: eine Anwendung pro Container - \end{itemize} -\end{enumerate} + \begin{itemize*} + \item VM Hypervisor: verstecken Hardware vor dem Betriebssystem + \item Docker: eine Anwendung pro Container + \end{itemize*} +\end{enumerate*} \subsubsection{Herausforderungen} \color{orange} Herausforderungen: \color{black} -\begin{itemize} +\begin{itemize*} \item viele verschiedene Computer/Server - \begin{itemize} - \item verschiedene Betriebssysteme - \item unterschiedliche Leistungsfähigkeit - \end{itemize} + \begin{itemize*} + \item verschiedene Betriebssysteme + \item unterschiedliche Leistungsfähigkeit + \end{itemize*} \item Systemkomponenten müssen miteinander kommunizieren \item verteilte Algorithmen: Nachrichten senden, empfangen, bestätigen, Synchronisation \item Knotenausfälle behandeln -\end{itemize} +\end{itemize*} \color{orange} $\Rightarrow$ brauchen Modelle zur Beschreibung der Kommunikation \color{black} \subsubsection{Anforderungen} \color{orange} Anforderungen an Kommunikationsmodelle in … \color{black} \newline … verteilten Systemen\newline \newline \begin{minipage}[h]{0.5\linewidth} - \begin{itemize} + \begin{itemize*} \item Korrektheit \item Sicherheit \item Verfügbarkeit \item Skalierbarkeit \item Heterogenität - \end{itemize} + \end{itemize*} \end{minipage} \begin{minipage}[h]{0.4\linewidth} \includegraphics[width=1.0\linewidth]{Assets/Programmierparadigmen-002} @@ -6287,52 +6137,52 @@ Wofür werden verteilte Systeme eingesetzt? \clearpage …verteilten Verkehrsmanagementsystemen\newline \newline \begin{minipage}[h]{0.5\linewidth} - \begin{itemize} + \begin{itemize*} \item Echtzeitfähigkeit \item Offenheit \item Korrektheit, \newline Sicherheit \item Skalierbarkeit, \newline Verfügbarkeit - \end{itemize} + \end{itemize*} \end{minipage} \begin{minipage}[h]{0.4\linewidth} \includegraphics[width=1.0\linewidth]{Assets/Programmierparadigmen-003} \end{minipage} \newline \newline \newline \newline \color{orange} Anforderungen \color{black} an den Betrieb eines (großen) verteilten Systems -\begin{itemize} +\begin{itemize*} \item (Last-)Skalierbarkeit (Scale-out): - \begin{itemize} - \item viele kleine Server - statt eines großen - \item neue Server nach Bedarf hinzuzufügen - \end{itemize} + \begin{itemize*} + \item viele kleine Server - statt eines großen + \item neue Server nach Bedarf hinzuzufügen + \end{itemize*} \item Funktionssicherheit (Safety) / IT-Sicherheit (Security) \item Fehlertoleranz / Verfügbarkeit - \begin{itemize} - \item Ausfälle von einzelnen Knoten kompensieren - \item Redundante Verarbeitung - \end{itemize} + \begin{itemize*} + \item Ausfälle von einzelnen Knoten kompensieren + \item Redundante Verarbeitung + \end{itemize*} \item Offenheit / Interoperabilität - \begin{itemize} - \item neue Knoten und Systeme einfach integrieren - \end{itemize} + \begin{itemize*} + \item neue Knoten und Systeme einfach integrieren + \end{itemize*} \item Transparenz - \begin{itemize} - \item verstecke die vielen Server vor den Nutzern - \end{itemize} -\end{itemize} + \begin{itemize*} + \item verstecke die vielen Server vor den Nutzern + \end{itemize*} +\end{itemize*} \subsection{Grundlagen verteilter Programmierung in Java und Erlang} \subsubsection{Sockets} -\begin{itemize} +\begin{itemize*} \item Verteilte Programmierung: Wir müssen einen entfernten Computer ansprechen \item benötigen: Adresse $\rightarrow$ IP-Adresse \item da mehrere Dienste auf demselben Computer laufen lauscht jeder Dienst auf einem Port (Nummer) - \begin{itemize} - \item Wichtige Ports: 80 WWW, 20 (FTP), 25 (SMTP) - \end{itemize} + \begin{itemize*} + \item Wichtige Ports: 80 WWW, 20 (FTP), 25 (SMTP) + \end{itemize*} \item \textbf{Socket} beschreibt einen Endpunkt, dh. Adresse und Port in einem TCP (oder UDP) Netzwerk \item \textbf{Server-Socket} wartet auf Verbindungen \item \textbf{Client} initiiert Verbindung, ebenfalls über einen (Client-Socket) -\end{itemize} +\end{itemize*} \subsubsection{Sockets in Java} Socket in dem package java.net.Socket einen \textbf{ServerSocket} auf Port 4242 erstellen \begin{center} @@ -6360,41 +6210,41 @@ Echo Server (Clientseite) \includegraphics[width=0.8\linewidth]{Assets/Programmierparadigmen-005} \end{center} \subsection{Aktormodell in Erlang} -\begin{itemize} +\begin{itemize*} \item formales Modell für Nebenläufigkeit und Verteilung \item Basis für verschiedene Programmiersprachen/Frameworks: Erlang, Akka (Scala/Java) \item Prinzipien: - \begin{itemize} - \item Aktor kapselt Zustand und Verhalten - \item Aktoren sind aktiv - \item Aktoren kommunizieren durch Nachrichtenaustausch - \begin{itemize} - \item Nichtblockierendes Senden - \item Blockierendes Empfangen - \end{itemize} - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Aktor kapselt Zustand und Verhalten + \item Aktoren sind aktiv + \item Aktoren kommunizieren durch Nachrichtenaustausch + \begin{itemize*} + \item Nichtblockierendes Senden + \item Blockierendes Empfangen + \end{itemize*} + \end{itemize*} +\end{itemize*} \subsubsection{Übersicht} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-006} \end{center} -\begin{itemize} +\begin{itemize*} \item Aktormodell in Erlang nativ umgesetzt - \begin{itemize} - \item Sende- und Empfangsoperationen schon für parallele Programmierung benutzt - \item bisher aber nur auf einem Knoten - \end{itemize} + \begin{itemize*} + \item Sende- und Empfangsoperationen schon für parallele Programmierung benutzt + \item bisher aber nur auf einem Knoten + \end{itemize*} \item Programmbestandteile im Aktormodell - \begin{itemize} - \item Verhaltensdefinition $\Rightarrow$ f() $\rightarrow$ … end. - \item Erzeugen neuer Aktoren $\Rightarrow$ Pid = spwan(fun …). - \item Empfangen von Nachrichten $\Rightarrow$ receive … end. - \item Senden $\Rightarrow$ Pid ! Request. - \end{itemize} + \begin{itemize*} + \item Verhaltensdefinition $\Rightarrow$ f() $\rightarrow$ … end. + \item Erzeugen neuer Aktoren $\Rightarrow$ Pid = spwan(fun …). + \item Empfangen von Nachrichten $\Rightarrow$ receive … end. + \item Senden $\Rightarrow$ Pid ! Request. + \end{itemize*} \item kein globaler Zustand -\end{itemize} +\end{itemize*} \subsubsection{Kommunikation zwischen Erlangknoten} Erlangknoten starten (sname = short name) \begin{center} @@ -6414,16 +6264,16 @@ Liste der verbundenen Knoten \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-010} \end{center} \subsubsection{Cookie-System} -\begin{itemize} +\begin{itemize*} \item verteilte Erlangknoten benötigen zur Kommunikation gemeinsames \color{orange} Magic Cookie \color{black} (Passwort) \item Mehrere Varianten - \begin{itemize} - \item Datei {\raise.17ex\hbox{$\scriptstyle\mathtt{\sim}$}}/.erlang.cookie - \item Erlang Funktion \newline - \color{blue} erlang:\color{OliveGreen}set\_cookie(node(), \color{blue}Cookie)\color{black} - \item Option: erl - setcookie Cookie - \end{itemize} -\end{itemize}\clearpage + \begin{itemize*} + \item Datei {\raise.17ex\hbox{$\scriptstyle\mathtt{\sim}$}}/.erlang.cookie + \item Erlang Funktion \newline + \color{blue} erlang:\color{OliveGreen}set\_cookie(node(), \color{blue}Cookie)\color{black} + \item Option: erl - setcookie Cookie + \end{itemize*} +\end{itemize*}\clearpage \subsubsection{Verbindungsaufbau zwischen Erlangknoten} Verbindungsaufbau mittels \color{blue} net\_adm:\color{black}ping Funktion \begin{center} @@ -6438,17 +6288,17 @@ Starten eines Prozesses auf einem entfernten Host \end{center}\clearpage \subsection{Alternating Bit Protokoll} \subsubsection{Übersicht} -\begin{itemize} +\begin{itemize*} \item ermöglicht es, Nachrichten über einen verlustbehafteten Kommunikationskanal vollständig zu übertragen, sofern Verluste nur gelegentlich auftreten (transiente Fehler) \item Empfänger quittiert jedes erhaltene Paket (Achnowledgement, kurz ACK) \item \textbf{Achtung} Kanal kann Nachrichten und ACKs verlieren - \begin{itemize} - \item benötigen je zwei unterschiedliche Sequenznummern und ACKs - \end{itemize} + \begin{itemize*} + \item benötigen je zwei unterschiedliche Sequenznummern und ACKs + \end{itemize*} \item Empfänger liefert eine Nachricht nur beim ersten Empfang aus (keine Duplikate) \item bei Timeout: Nachricht erneut senden \item bei Erhalt eines unerwarteten ACKs: \textbf{aktuelle Nachricht erneut senden} -\end{itemize} +\end{itemize*} \subsubsection{Zustände} \begin{center} \centering @@ -6456,23 +6306,23 @@ Starten eines Prozesses auf einem entfernten Host \end{center} \subsubsection{Das Alternating Bit Protokoll} Wir implementieren eine Variante, bei welcher: -\begin{itemize} +\begin{itemize*} \item der Sender zu Beginn eine Liste mit sämtlichen zu sendenden Nachrichten erhält, und \item der Empfänger die erstmals empfangenen Nachrichten einfach auf dem Bildschirm ausgibt \item alle Aktoren Statusmeldungen ausgeben \item Verluste über einen Zufallszahlengenerator ausgelöst werden -\end{itemize} +\end{itemize*} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-0014} \end{center} drei Prozesse mit \textbf{initialize(ErrorRate, NumberOfMessages, ReceiverPid, SenderPid, ChannelPid)} initialisieren und starten -\begin{itemize} +\begin{itemize*} \item Sender hat vier Zustandsfunktionen; Startet mit senderReady0(List): Liste mit Zahlen 1,…,NumberOfMessages \item Kanal: Nachricht "verlieren", wenn Zufallszahl $\ngeq$ ErrorRate \item Empfänger hat zwei Zustandsfunktionen; zu Beginn wird receiverWait0 gestartet \item initialize wartet auf eine ready-Nachricht; sendet danach stop-Nachrichten an alle -\end{itemize} +\end{itemize*} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-0015} @@ -6504,71 +6354,71 @@ drei Prozesse mit \textbf{initialize(ErrorRate, NumberOfMessages, ReceiverPid, S \subsection{Kommunikationsmodelle \& Implementierungen} \subsubsection{Kommunikationsmodelle} Frage: Wie sollen Knoten miteinander kommunizieren? -\begin{itemize} +\begin{itemize*} \item Sprechen die Teilnehmer direkt miteinander oder über einen Vermittler? \item Kann jeder jedem eine Nachricht schicken? \item Wartet ein Teilnehmer darauf, dass seine Nachricht angekommen ist? \item Wartet ein Teilnehmer darauf, dass eine Nachricht ankommt? \item Muss ein Teilnehmer auf eine Nachricht antworten? -\end{itemize} +\end{itemize*} $\Rightarrow$ das Verhalten der Teilnehmer ist in \color{orange} Kommunikationsmodellen \color{black} beschrieben \subsubsection{Arten von Kommunikationsmodellen} Es gibt viele verschiedene Modelle, z.B. für \color{orange} Botschaftenbasierte Modelle \color{black} -\begin{itemize} +\begin{itemize*} \item Auftragsorientierte Modelle \item Funktionsaufrufbasierte Modelle \item Blackboards \item Ereignisbasierte Modelle \item Strombasierte Modelle \item Wissensbasierte Modelle -\end{itemize} +\end{itemize*} Kommunikationspartner sind für uns: -\begin{itemize} +\begin{itemize*} \item Threads/Prozesse innerhalb verteilter Anwendungen \item Komponenten verteilter Systeme (Browser $\Leftrightarrow$ Webserver, DB Client $\Leftrightarrow$ DB-Server) -\end{itemize} +\end{itemize*} \subsubsection{Modellbestandteile} -\begin{itemize} +\begin{itemize*} \item \color{orange} Rollenmodell: \color{black} - \begin{itemize} - \item gemeinsames Handlungsmuster festlegen - \item z.B. Anrufer/Angerufener, Clinet/Server, Quelle/Senke - \end{itemize} + \begin{itemize*} + \item gemeinsames Handlungsmuster festlegen + \item z.B. Anrufer/Angerufener, Clinet/Server, Quelle/Senke + \end{itemize*} \item \color{orange} Datenmodell: \color{black} - \begin{itemize} - \item einheitliche Interpretation der ausgetauschten Daten - \item z.B. Dateiformate (XML/JSON), Kodierungen (MPEG4/H.264) - \end{itemize} + \begin{itemize*} + \item einheitliche Interpretation der ausgetauschten Daten + \item z.B. Dateiformate (XML/JSON), Kodierungen (MPEG4/H.264) + \end{itemize*} \item \color{orange} Fehlersemantiken \color{black} - \begin{itemize} - \item Einvernehmen über Wirkungen von Ausfällen - \item Eigenschaften von Kommunikationsoperationen müssen bei Ausfällen garantiert werden - \end{itemize} + \begin{itemize*} + \item Einvernehmen über Wirkungen von Ausfällen + \item Eigenschaften von Kommunikationsoperationen müssen bei Ausfällen garantiert werden + \end{itemize*} \item \color{orange} Terminierungssemantik \color{black} - \begin{itemize} - \item Einvernehmen über das Ende der Kommunikation - \item Garantien über das Ende von Kommunikationsoperationen (auch bei Ausfällen) - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Einvernehmen über das Ende der Kommunikation + \item Garantien über das Ende von Kommunikationsoperationen (auch bei Ausfällen) + \end{itemize*} +\end{itemize*} \subsubsection{Kommunikationsarten} -\begin{itemize} +\begin{itemize*} \item Wann ist eine Kommunikationsoperation abgeschlossen? \item entspricht Terminierungssemantik \item zwei grundlegende Arten: - \begin{itemize} - \item \color{orange} synchron \color{black} - \begin{itemize} - \item blockierend - \item Teilnehmer wartet bis die Gegenseite bereit ist - \end{itemize} - \item \color{orange} asynchron \color{black} - \begin{itemize} - \item nicht-blockierend - \item Der Teilnehmer wartet nicht auf die Gegenseite - \end{itemize} - \item gilt sowohl für das Senden als auch das Empfangen - \end{itemize} -\end{itemize} + \begin{itemize*} + \item \color{orange} synchron \color{black} + \begin{itemize*} + \item blockierend + \item Teilnehmer wartet bis die Gegenseite bereit ist + \end{itemize*} + \item \color{orange} asynchron \color{black} + \begin{itemize*} + \item nicht-blockierend + \item Der Teilnehmer wartet nicht auf die Gegenseite + \end{itemize*} + \item gilt sowohl für das Senden als auch das Empfangen + \end{itemize*} +\end{itemize*} \subsubsection{Kommunikationsarten: Senden} \color{orange} synchrones Senden: \color{black} Der Sender wartet bis der Empfänger die Botschaft annimmt \begin{center} @@ -6581,18 +6431,18 @@ Kommunikationspartner sind für uns: \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-0025} \end{center} \subsubsection{Synchrones vs. asynchrones Senden} -\begin{itemize} +\begin{itemize*} \item synchrones Senden - \begin{itemize} - \item kann lange dauern, der Sender kann währenddessen nicht weiterarbeiten - \item die Botschaftenankunft ist garantiert, eine einfache Implementierung synchroner Aktivitäten - \end{itemize} + \begin{itemize*} + \item kann lange dauern, der Sender kann währenddessen nicht weiterarbeiten + \item die Botschaftenankunft ist garantiert, eine einfache Implementierung synchroner Aktivitäten + \end{itemize*} \item asynchrones Senden - \begin{itemize} - \item unklar ob die Botschaft angekommen ist - \item einfache Implementierung von Nebenläufigkeit - \end{itemize} -\end{itemize} + \begin{itemize*} + \item unklar ob die Botschaft angekommen ist + \item einfache Implementierung von Nebenläufigkeit + \end{itemize*} +\end{itemize*} \subsubsection{Kommunikationsarten: Empfangen} \color{orange} synchrones Empfangen: \color{black} Der Empfänger wartet bis die Botschaft eintrifft \begin{center} @@ -6605,36 +6455,36 @@ Kommunikationspartner sind für uns: \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-0027} \end{center} \subsubsection{Synchrones vs. asynchrones Empfangen} -\begin{itemize} +\begin{itemize*} \item synchrones Empfangen: - \begin{itemize} - \item kann lange dauern, der Sender kann nicht weiterarbeiten - \item Botschaftenankunft ist einfach und präzise feststellbar - \end{itemize} + \begin{itemize*} + \item kann lange dauern, der Sender kann nicht weiterarbeiten + \item Botschaftenankunft ist einfach und präzise feststellbar + \end{itemize*} \item asynchrones Empfangen: - \begin{itemize} - \item unklar wann die Botschaft ankommt; - \newline Benachrichtigungstechniken - \begin{itemize} - \item Nachfragen (Polling) - \item ankommende Botschaft erzeugt neuen Thread beim Empfänger - \item weitere Techniken möglich - \end{itemize} - \item einfache Implementierung von Nebenläufigkeit - \end{itemize} -\end{itemize}\clearpage + \begin{itemize*} + \item unklar wann die Botschaft ankommt; + \newline Benachrichtigungstechniken + \begin{itemize*} + \item Nachfragen (Polling) + \item ankommende Botschaft erzeugt neuen Thread beim Empfänger + \item weitere Techniken möglich + \end{itemize*} + \item einfache Implementierung von Nebenläufigkeit + \end{itemize*} +\end{itemize*}\clearpage \subsubsection{Fehlerbehandlung} -\begin{itemize} +\begin{itemize*} \item unverlässliches vs. verlässliches Senden - \begin{itemize} - \item "Brief vs. Einschreiben" - \end{itemize} + \begin{itemize*} + \item "Brief vs. Einschreiben" + \end{itemize*} \item verlässliche Kommunikation erfordert - \begin{itemize} - \item Quittierungen (Acknowledgements) $\rightarrow$ mehr Daten senden - \item Timeouts $\rightarrow$ Zeitverwaltung, langes Warten - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Quittierungen (Acknowledgements) $\rightarrow$ mehr Daten senden + \item Timeouts $\rightarrow$ Zeitverwaltung, langes Warten + \end{itemize*} +\end{itemize*} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-0028} @@ -6646,130 +6496,130 @@ Kommunikationspartner sind für uns: \end{center} vielfältige Fehlermöglichkeiten in verteilten Anwendungen: -\begin{itemize} +\begin{itemize*} \item Kommunikations-/Netzwerkfehler: $\rightarrow$ Nachricht/Antwort gar nicht oder nur verzögert zugestellt \item Serverausfall: Nachricht empfangen? Operation ausgeführt? \item Clientausfall: Aufruf gültig? Bestätigung erhalten? \item Beispiel: Reisebuchung - \begin{itemize} - \item Buchung durchgeführt? Bestätigung erhalten? - \item Bei wiederholter Ausführung: wirklich neue Buchung? - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Buchung durchgeführt? Bestätigung erhalten? + \item Bei wiederholter Ausführung: wirklich neue Buchung? + \end{itemize*} +\end{itemize*} \subsubsection{Fehlerbehandlung in Erlang} -\begin{itemize} +\begin{itemize*} \item Timeout beim Warten auf Nachrichten - \begin{itemize} - \item Wenn keine passende Nachricht innerhalb \textbf{Time} msecs empfangen wird, dann wird der Rückgabewert des \textbf{after}-Ausdrucks verwendet. - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Wenn keine passende Nachricht innerhalb \textbf{Time} msecs empfangen wird, dann wird der Rückgabewert des \textbf{after}-Ausdrucks verwendet. + \end{itemize*} +\end{itemize*} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-030} \end{center} \subsubsection{Überwachung von Erlang-Prozessen} -\begin{itemize} +\begin{itemize*} \item Linking von Prozessen: \color{green}link\color{blue}(Pid) \color{black} \item M überwacht S; S bricht durch Fehler ab - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-031} - \end{center} - + \begin{center} + \centering + \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-031} + \end{center} + \item M wartet auf EXIT Nachricht von S $\rightarrow$ asynchroner Handler nötig -\end{itemize} +\end{itemize*} \subsubsection{on\_exit-Handler} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-032} \end{center} -\begin{itemize} +\begin{itemize*} \item überwacht den Prozess \textbf{Pid} auf Abbruch \item Anwendungsspezifische Reaktionen möglich - \begin{itemize} - \item Fehlermeldung - \item Neustart des Prozesses - \end{itemize} + \begin{itemize*} + \item Fehlermeldung + \item Neustart des Prozesses + \end{itemize*} \item auch über Erlang-Knotengrenzen hinweg! -\end{itemize}\clearpage +\end{itemize*}\clearpage \subsubsection{Anwendung des on\_exit-Handlers} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-033} \end{center} -\begin{itemize} +\begin{itemize*} \item Funktion anlegen (Liste in Atom konvertieren) \item Prozess erzeugen \item \textbf{on\_exit}-Handler definieren \item Fehler verursachen (Nachricht ist keine Liste) -\end{itemize} +\end{itemize*} \subsubsection{Fehlersemantiken} Umgang mit Fehlern (Timeouts, Ausfälle) -\begin{itemize} +\begin{itemize*} \item \color{orange} Maybe: \color{black} - \begin{itemize} - \item keine Wiederholung - \item keine Ausführungsgarantie - \end{itemize} + \begin{itemize*} + \item keine Wiederholung + \item keine Ausführungsgarantie + \end{itemize*} \item \color{orange} At-least-once: \color{black} - \begin{itemize} - \item wiederholte Ausführung, aber keine Erkennung von Nachrichtenduplikaten - \item nur für idempotente Optionen (Lesen) - \end{itemize} + \begin{itemize*} + \item wiederholte Ausführung, aber keine Erkennung von Nachrichtenduplikaten + \item nur für idempotente Optionen (Lesen) + \end{itemize*} \item \color{orange} At-most-once: \color{black} - \begin{itemize} - \item garantiert, dass mehrfache Aufrufe nur zu einziger Ausführung führen - \item z.B. durch Sequenznummern (erfordert Protokollierung zur Duplikatelliminierung) - \item für nicht-idempotente Operationen (schreibend, z.B. Einfügen, Löschen) - \end{itemize} -\end{itemize}\clearpage + \begin{itemize*} + \item garantiert, dass mehrfache Aufrufe nur zu einziger Ausführung führen + \item z.B. durch Sequenznummern (erfordert Protokollierung zur Duplikatelliminierung) + \item für nicht-idempotente Operationen (schreibend, z.B. Einfügen, Löschen) + \end{itemize*} +\end{itemize*}\clearpage \subsubsection{Auftragsorientierte Modelle} -\begin{itemize} +\begin{itemize*} \item klassische Modell serviceorientierten Systemdesigns \item in verteilten Systemen: - \begin{itemize} - \item Menge von Dienstanbietern (Server) - \item Menge von Clients, die diese Dienste nutzen wollen - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Menge von Dienstanbietern (Server) + \item Menge von Clients, die diese Dienste nutzen wollen + \end{itemize*} +\end{itemize*} Typische Anwendungsszenarien -\begin{itemize} +\begin{itemize*} \item \textbf{DB-Server}: verwalten Datenbestände, verarbeiten SQL Anfragen - \begin{itemize} - \item Clients: "Gib mir alle Personen, die älter als 18 Jahre alt sind" - \end{itemize} + \begin{itemize*} + \item Clients: "Gib mir alle Personen, die älter als 18 Jahre alt sind" + \end{itemize*} \item \textbf{Web}: Webserver stellt HTML Dokumente bereit, Brwoser ruft URLs für Dokumente auf \item \textbf{E-Mail}: Mailserver verwalten Postfächer, leiten Mails weiter, Outlook/Thunderbird/…senden/lesen von Emails \item Namensdienste (DNS), Fileserver, Zeitserver (NTP) -\end{itemize} +\end{itemize*} \subsubsection{Auftragsorientierte Modelle: Modellsicht} -\begin{itemize} +\begin{itemize*} \item Rollenmodell: Clients erteilen Aufträge an Server \item Datenmodell: Notschaften mit vereinbarter Struktur (Protokoll) -\end{itemize} +\end{itemize*} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-034} \end{center} -\begin{itemize} +\begin{itemize*} \item Fehlersemantiken: Was ist der Grund, wenn ich keine Antwort erhalte? - \begin{itemize} - \item Auftrag angekommen? Vollständig bearbeitet? - \item Was passiert wenn ein Auftrag wiederholt wird? - \end{itemize} + \begin{itemize*} + \item Auftrag angekommen? Vollständig bearbeitet? + \item Was passiert wenn ein Auftrag wiederholt wird? + \end{itemize*} \item Terminierungssemantiken: - \begin{itemize} - \item Auftragserteilung in der Regel synchron - \item es existieren aber auch asynchrone Aufträge - \end{itemize} -\end{itemize}\clearpage + \begin{itemize*} + \item Auftragserteilung in der Regel synchron + \item es existieren aber auch asynchrone Aufträge + \end{itemize*} +\end{itemize*}\clearpage \subsubsection{Auftragsorientierte Modelle: Implementierung} -\begin{itemize} +\begin{itemize*} \item Implementierung aufbauend auf send/receive -\end{itemize} +\end{itemize*} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-035} @@ -6780,164 +6630,164 @@ Typische Anwendungsszenarien \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-036} \end{center} \subsubsection{Erläuterungen zum Server} -\begin{itemize} +\begin{itemize*} \item Zeile 1 \& 2: Serversocket erstellen, lauscht auf Port 4242, wartet blockierend bis sich ein Client verbindet \item Zeile 6: lies eine Zeile vom Client \item Zeile 7: unser Nachrichtenformat: \textbf{Operation $<$Leerzeichen$>$ Dateipfad} \item Zeile 8ff: unterscheide Operationen und führe Aktionen aus; antworte dem Client entsprechend -\end{itemize} +\end{itemize*} \subsubsection{Ein Fileserver in Java - Client} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-037} \end{center} -\begin{itemize} +\begin{itemize*} \item Zeile 1: erstelle Clientsocket, d.h. Verbindungsaufbau zum Server auf localhost auf Port 4242 \item Zeile 2: lese Befehl und Dateipfad \item Zeile 4: sende Befehl als String an den Server \item Zeile 6ff: lese alle Antwortzeilen vom Server; Ausgabe auf dem Bildschirm -\end{itemize} +\end{itemize*} \subsubsection{Auftragsorientierte Modelle} -\begin{itemize} +\begin{itemize*} \item Können benutzt werden, um einfache Protokolle zu implementieren \item Binär oder ASCII - \begin{itemize} - \item auch Übertragung komplexer Objekte möglich - \end{itemize} + \begin{itemize*} + \item auch Übertragung komplexer Objekte möglich + \end{itemize*} \item gesendeter Befehl könnte einer Methode/Funktion auf dem Server entsprechen - \begin{itemize} - \item es erfolgt eine Art entfernter Funktionsaufruf - \item RPC wird im nächsten Abschnitt behandelt - \end{itemize} + \begin{itemize*} + \item es erfolgt eine Art entfernter Funktionsaufruf + \item RPC wird im nächsten Abschnitt behandelt + \end{itemize*} \item Funktionalität kann über das Internet angeboten werden - \begin{itemize} - \item[$\Rightarrow$] Implementierung eines Webservices - \end{itemize} -\end{itemize} + \begin{itemize*} + \item[$\Rightarrow$] Implementierung eines Webservices + \end{itemize*} +\end{itemize*} \subsubsection{Webservices - Allgemein} -\begin{itemize} +\begin{itemize*} \item WebService: Dienst, der über das Internet/WWW von Clients angesprochen werden kann \item typischerweise über HTTP \item Früher \textbf{SOAP}: Simple Object Access Protocol - \begin{itemize} - \item Protokoll zum Austausch von Informationen in XML - \item Verzeichnisdienste zum Finden von Diensten, z.B. UDDI - \end{itemize} + \begin{itemize*} + \item Protokoll zum Austausch von Informationen in XML + \item Verzeichnisdienste zum Finden von Diensten, z.B. UDDI + \end{itemize*} \item Heute \textbf{REST} -\end{itemize} +\end{itemize*} \subsubsection{REST} -\begin{itemize} +\begin{itemize*} \item Die Grundidee von REST: - \begin{itemize} - \item \textbf{REST}: Representional State Transfer - \item oftmals existiert ein HTTP Server / Anwendungsserver schon - \item Idee: Jede Ressource die vom Server angeboten wird, ist durch eine URI beschrieben/identifiziert - \begin{itemize} - \item Datei, ein Eintrag in einer Datenbank, Tweet,… - \end{itemize} - \item Anlegen, Lesen, Verändern, Löschen (CRUD) - \begin{itemize} - \item Art der Operation über HTTP Request-Typ festlegen (POST, GET, PUT, DELETE) - \end{itemize} - \item Unabhängigkeit von verwendeter Programmiersprache in Client und Server durch HTTP und Textformate - \end{itemize} -\end{itemize} + \begin{itemize*} + \item \textbf{REST}: Representional State Transfer + \item oftmals existiert ein HTTP Server / Anwendungsserver schon + \item Idee: Jede Ressource die vom Server angeboten wird, ist durch eine URI beschrieben/identifiziert + \begin{itemize*} + \item Datei, ein Eintrag in einer Datenbank, Tweet,… + \end{itemize*} + \item Anlegen, Lesen, Verändern, Löschen (CRUD) + \begin{itemize*} + \item Art der Operation über HTTP Request-Typ festlegen (POST, GET, PUT, DELETE) + \end{itemize*} + \item Unabhängigkeit von verwendeter Programmiersprache in Client und Server durch HTTP und Textformate + \end{itemize*} +\end{itemize*} \subsubsection{Anforderungen an Ressourcen} Anforderungen an Ressourcen nach Fielding: -\begin{enumerate} +\begin{enumerate*} \item Adressierbarkeit: jede Ressource muss über URI adressierbar sein (Achtung: URI != URL, Identifier vs. Locator) \item Zustandslosigkeit: Kommunikation zwischen Client und Server hat keinen Zustand (Session/Cookie) - \begin{itemize} - \item bei jeder Anfrage werden alle Informationen gesendet - \end{itemize} + \begin{itemize*} + \item bei jeder Anfrage werden alle Informationen gesendet + \end{itemize*} \item Einheitliche Schnittstelle: über HTTP Standardmethoden auf Ressourcen zugreifen \item Entkopplung von Ressource und Repräsentation: Ressourcen können in verschiedenen Formaten angeboten werden (JSON, XML,…) -\end{enumerate} +\end{enumerate*} \subsubsection{HTTP Methoden für REST} -\begin{itemize} +\begin{itemize*} \item selbe URL mit verschiedenen Methoden aufrufbar \item Methode bestimmt ausgeführte Aktion auf dem Server -\end{itemize} -\begin{itemize} +\end{itemize*} +\begin{itemize*} \item[GET:] eine Ressource lese, Daten sollten nicht verändert werden \item[POST:] neue Ressource erstellen - \begin{itemize} - \item Die URI ist dem Anrufer zunächst unbekannt - \item Der Server kann dem Anrufer die erzeugte URI in der Antwort mitteilen - \end{itemize} + \begin{itemize*} + \item Die URI ist dem Anrufer zunächst unbekannt + \item Der Server kann dem Anrufer die erzeugte URI in der Antwort mitteilen + \end{itemize*} \item[PUT:] neue Ressource erstellen, oder existierende bearbeiten \item[DELETE:] zum Löschen von Ressourcen -\end{itemize} +\end{itemize*} \subsubsection{REST - Beispiel} Spotify API -\begin{itemize} +\begin{itemize*} \item \textbf{Authorization}-Header benötigt \item \textbf{id}: Spotify-ID eines Künstlers -\end{itemize} +\end{itemize*} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-038} \end{center} \subsubsection{Implementierung von RESTful Webservices} -\begin{itemize} +\begin{itemize*} \item manuelle Implementierung recht aufwändig - \begin{itemize} - \item unterscheiden von HTTP Methoden (GET, POST,…) - \item parsen/prüfen von URL Pfaden und Parametern - \item setzen von Antwortheadern \& Kodierung in XML/JSON - \end{itemize} + \begin{itemize*} + \item unterscheiden von HTTP Methoden (GET, POST,…) + \item parsen/prüfen von URL Pfaden und Parametern + \item setzen von Antwortheadern \& Kodierung in XML/JSON + \end{itemize*} \item REST Frameworks erleichtern die Arbeit deutlich - \begin{itemize} - \item JAX-RS Spezifikation für Java zur Erstellung von RESTful Services - \begin{itemize} - \item Implementierung: Jersey: https://eclipse-ee4j.github.io/jersey/ - \item Implementierung: Spring: - https://spring.io/guides/gs/rest-service/ - \end{itemize} - \item Microsofts \textbf{cpprestsdk} für C++ als Client-Bibliothek: - https://github.com/Microsoft/cpprestsdk - \end{itemize} -\end{itemize} -\begin{itemize} + \begin{itemize*} + \item JAX-RS Spezifikation für Java zur Erstellung von RESTful Services + \begin{itemize*} + \item Implementierung: Jersey: https://eclipse-ee4j.github.io/jersey/ + \item Implementierung: Spring: + https://spring.io/guides/gs/rest-service/ + \end{itemize*} + \item Microsofts \textbf{cpprestsdk} für C++ als Client-Bibliothek: + https://github.com/Microsoft/cpprestsdk + \end{itemize*} +\end{itemize*} +\begin{itemize*} \item Beispiel: Jersey \item Definition einer einfachen Klasse - \begin{itemize} - \item Einstellungen über Annotationen - \item Klasse muss als Servlet in einem Applicationserver ausgeführt werden - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Einstellungen über Annotationen + \item Klasse muss als Servlet in einem Applicationserver ausgeführt werden + \end{itemize*} +\end{itemize*} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-039} \end{center} \subsubsection{Restful Webservice - Erläuterungen} -\begin{itemize} +\begin{itemize*} \item Zeile 1: dieser Dienst ist über den Pfad files erreichbar, - z.B. http://localhost/files + z.B. http://localhost/files \item Zeile 3: die nachfolgende Methode soll HTTP GET Anfragen - verarbeiten + verarbeiten \item Zeile 4: die URL enthält den Dateinamen als - Pfad-Bestandteil, z.B. - http://localhost/files/myfile.txt + Pfad-Bestandteil, z.B. + http://localhost/files/myfile.txt \item Zeile 5: Hinweis an das Jersey-Framework das Ergebnis - automatisch ins JSON Format umzuwandeln + automatisch ins JSON Format umzuwandeln \item Zeile 6: normale Definition einer Methode \& Mapping des - Eingabeparameters auf den URL-Parameter + Eingabeparameters auf den URL-Parameter \item Zeile 8: das infos Objekt vom Typ FileInfo wird - automatisch als JSON repräsentiert -\end{itemize} + automatisch als JSON repräsentiert +\end{itemize*} \subsubsection{Aufruf von REST-Services} https://reques.in kostenloser Dienst zum Testen von REST-Clients -\begin{itemize} +\begin{itemize*} \item Variante 1: telnet reques.in 80 … \item Variante 2: Auf der Kommandozeile \newline - \$ curl https://reqres.in/api/users/1 - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-040} - \end{center} + \$ curl https://reqres.in/api/users/1 + \begin{center} + \centering + \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-040} + \end{center} \item Variante 3: Aufruf in einem Programm -\end{itemize}\clearpage +\end{itemize*}\clearpage \subsubsection{HTTP GET Aufrufe in Java} In Java ab Version 11 eingebauter HTTP Client \begin{center} @@ -6956,35 +6806,35 @@ Antwort: \end{center} Eigentlich: JSON Ergebnis mit geeigneten Frameworks parsen und weiterverarbeiten \subsubsection{Zusammenfassung} -\begin{itemize} +\begin{itemize*} \item Auftragsorientierte Modelle nach dem Client-Server Prinzip \item WebServices bieten Dienste über das WWW an \item RESTful WebServices - \begin{itemize} - \item jede Ressource hat eine URI - \item HTTP Methoden für Aktionen auf Ressourcen - \item unabhängig von Programmiersprachen - \end{itemize} -\end{itemize} + \begin{itemize*} + \item jede Ressource hat eine URI + \item HTTP Methoden für Aktionen auf Ressourcen + \item unabhängig von Programmiersprachen + \end{itemize*} +\end{itemize*} \subsubsection{Funktionsaufrufbasierte Protokolle} -\begin{itemize} +\begin{itemize*} \item Grundidee: Adaption von anwendungsnahen und unkomplizierten Kommunikationsparadigmen an Eigenschaften verteilter Systeme \item d.h., aus Aufrufen auf lokalen Prozeduren und Methoden werden Aufrufe entfernter Prozeduren und Methoden \item bekannt als: - \begin{itemize} - \item RPC: Remote Procedure Calls - \item oder Java RMI: Remote Method Invocation - \end{itemize} + \begin{itemize*} + \item RPC: Remote Procedure Calls + \item oder Java RMI: Remote Method Invocation + \end{itemize*} \item Erlang und Java haben die Konzepte nativ implementiert, in C++ nur über zusätzliche Bibliotheken -\end{itemize} +\end{itemize*} \subsubsection{Eigenschaften von Prozedurfernaufrufen} Aufruf und Ausführung in unterschiedlichen Umgebungen/Kontexten -\begin{itemize} +\begin{itemize*} \item Programmiersprachen \item Namens- und Adressräume \item Betriebssystemkontext \item Hardwarekontext -\end{itemize} +\end{itemize*} Woher kennt der Aufrufer die Signatur der Prozedur auf dem Server? $\Rightarrow$ \color{orange} Stubs \color{black} \begin{center} \centering @@ -6992,48 +6842,48 @@ Woher kennt der Aufrufer die Signatur der Prozedur auf dem Server? $\Rightarrow$ \end{center} \subsubsection{RPC: Stubs} Ein Stub hat verschiedene Aufgaben: -\begin{itemize} +\begin{itemize*} \item wandelt lokalen Prozeduraufruf in Netzwerkfunktion um \item Ein- und Auspacken von Argumenten und Ergebnissen \item Anpassung von Datenrepräsentationen \item implementiert Übertragungsprotokoll über das Netzwerk -\end{itemize} +\end{itemize*} Der Server-Stub/Skeleton -\begin{itemize} +\begin{itemize*} \item wartet auf Anfragen von Clients \item übernimmt sonst gleiche Aufgaben wie Client-Stub -\end{itemize} +\end{itemize*} \subsubsection{RPC in Erlang} -\begin{itemize} +\begin{itemize*} \item Vordefiniertes Erlang-Modul für RPC - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-045} - \end{center} + \begin{center} + \centering + \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-045} + \end{center} \item führt \textbf{Module:Func(Args)} auf \textbf{Node} aus - \begin{itemize} - \item weitere Funktionen für asynchrone Aufrufe, Aufrufe von mehreren Servern - \end{itemize} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-046} - \end{center} + \begin{itemize*} + \item weitere Funktionen für asynchrone Aufrufe, Aufrufe von mehreren Servern + \end{itemize*} + \begin{center} + \centering + \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-046} + \end{center} \item andere Möglichkeit: eigene Funktionen über \textbf{register} anmelden (siehe Alternating Bit Protokoll) \item mit \textbf{whereis} PID von registrierten Erlang-Prozessen finden -\end{itemize} +\end{itemize*} \subsubsection{RMI: Javas RPC Variante} -\begin{itemize} +\begin{itemize*} \item seit Java 5 nativ in die Sprache eingebaut - keine explizite Generierung von Stubs notwendig \item Aufruf von Objektmethoden: - \begin{itemize} - \item Server: Objekte mit Zuständen - \item Objekte können als Methoden-Argumente und Ergebnisse verwendet werden - \end{itemize} + \begin{itemize*} + \item Server: Objekte mit Zuständen + \item Objekte können als Methoden-Argumente und Ergebnisse verwendet werden + \end{itemize*} \item entfernt aufrufbare Methoden in einem Java-interface definieren - \begin{itemize} - \item abgeleitet von \textbf{java.rmi.Remote} - \end{itemize} -\end{itemize} + \begin{itemize*} + \item abgeleitet von \textbf{java.rmi.Remote} + \end{itemize*} +\end{itemize*} \subsubsection{RMI - Schnittstelle für entfernte Objekte} \begin{center} \centering @@ -7041,11 +6891,11 @@ Der Server-Stub/Skeleton \end{center}\clearpage \subsubsection{RMI: Server} Server-Objekt muss: -\begin{itemize} +\begin{itemize*} \item Remote-Schnittstelle implementieren \item im RMI-Laufzeitsystem bekannt gemacht werden \item im Namensverzeichnis registriert werden -\end{itemize} +\end{itemize*} Server-Objekt anlegen: \begin{center} \centering @@ -7057,11 +6907,11 @@ Server-Objekt anlegen: \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-049} \end{center}\clearpage \subsubsection{RMI - Client} -\begin{itemize} +\begin{itemize*} \item über Namensdienst Server-Objekt finden \item Stub erzeugen (erfolgt automatisch von JVM) \item Methode auf dem Server-Objekt aufrufen -\end{itemize} +\end{itemize*} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-050} @@ -7073,29 +6923,29 @@ Server-Objekt anlegen: \end{center} \subsubsection{Interoperabilität von RPC} Problem von Erlang, Java RMI, etc.: -\begin{itemize} +\begin{itemize*} \item an Programmiersprache gebunden $\rightarrow$ verschiedene Systeme können nicht miteinander verbunden werden -\end{itemize} +\end{itemize*} Lösungsansätze: -\begin{itemize} +\begin{itemize*} \item \textbf{XML-RPC, JSON-RPC}: kodiere alle zum Aufruf nötigen Informationen als XML bzw. JSON - \begin{itemize} - \item HTTP zur Übertragung - \item \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-052} - \item \textbf{id} für die Zuordnung von Antworten zu Anfragen - \end{itemize} + \begin{itemize*} + \item HTTP zur Übertragung + \item \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-052} + \item \textbf{id} für die Zuordnung von Antworten zu Anfragen + \end{itemize*} \item \textbf{gRPC}: Code-Generierung für Server, Stubs und ausgetauschte Daten -\end{itemize} +\end{itemize*} \subsubsection{gRPC} -\begin{itemize} +\begin{itemize*} \item initiiert von Google im Jahr 2015 \item plattformunabhängige Beschreibung von Daten und Diensten -\end{itemize} +\end{itemize*} \color{orange} \textbf{gRPC} \color{black} -\begin{itemize} +\begin{itemize*} \item ProtoBuf Dateien übersetzt in konkrete Programmiersprache \item C/C++, Java, Python, GO, uvm. -\end{itemize} +\end{itemize*} \begin{center} \centering \includegraphics[width=0.45\linewidth]{Assets/Programmierparadigmen-053} @@ -7107,29 +6957,29 @@ fileservice.proto \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-054} \end{center}\clearpage \subsubsection{gRPC: Dienstbeschreibung - Erläuterungen} -\begin{itemize} +\begin{itemize*} \item Datenklasse \textbf{FileInfo} mit drei Attributen, Zahlen geben Reihenfolge bei Serialisierung an \item \textbf{Request:} Service darf nur eine Eingabe- und Ausgabe-Message haben - \begin{itemize} - \item extra Typen für Parameter und Ergebnis erlauben einfache Erweiterung ohne Signaturen zu ändern - \end{itemize} + \begin{itemize*} + \item extra Typen für Parameter und Ergebnis erlauben einfache Erweiterung ohne Signaturen zu ändern + \end{itemize*} \item \textbf{FileService:} Klasse die unseren Dienst darstellt; enthält eine Methode \textbf{GetDetail} die von Clients aufgerufen werden kann -\end{itemize} +\end{itemize*} \subsubsection{gRPC: Dienstbeschreibung} -\begin{itemize} +\begin{itemize*} \item *.proto Dateien werden mittels protoc Compiler in die Zielsprache übersetzt - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-055} - \end{center} + \begin{center} + \centering + \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-055} + \end{center} \item erzuegt C++ Dateien für messages sowie Service-Klassen (FileService) \item Klasse \textbf{FileService} enthält generierten Stub und Methoden für den Server zum überschreiben -\end{itemize} +\end{itemize*} \subsubsection{gRPC: Server erzeugen} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-056} -\end{center} +\end{center} \subsubsection{gRPC: Server starten} \begin{center} \centering @@ -7141,139 +6991,139 @@ fileservice.proto \includegraphics[width=0.65\linewidth]{Assets/Programmierparadigmen-058} \end{center} \subsubsection{Zusammenfassung} -\begin{itemize} +\begin{itemize*} \item Funktionsaufrufbasierte Modelle: - \newline Prozedur/Funktion/Methode auf einem entfernten Host aufrufen + \newline Prozedur/Funktion/Methode auf einem entfernten Host aufrufen \item Stubs kapseln Kommunikationsoperationen von Anwendung \item einheitliches Dateiformat notwendig \item Java RMI \item gRPC für Interoperabilität verschiedener Plattformen/Sprachen -\end{itemize} +\end{itemize*} \subsection{Weitere Kommunikationsmodelle und Cloud-Computing} \subsubsection{Blackboards} -\begin{itemize} +\begin{itemize*} \item das "schwarze Brett": Teilnehmer hinterlegen - \begin{itemize} - \item Gesuche und Angebote - \item Aufträge und Ergebnisse - \end{itemize} + \begin{itemize*} + \item Gesuche und Angebote + \item Aufträge und Ergebnisse + \end{itemize*} \item zeitliche und räumliche Entkopplung autonomer und anonymer Komponenten \item implementiert zum Beispiel in JavaSpaces -\end{itemize} +\end{itemize*} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-059} \end{center} \subsubsection{Blackboards: Modell-Sicht} -\begin{itemize} +\begin{itemize*} \item Rollenmodell: - \begin{itemize} - \item Spezialist (Worker): aktiver Nutzer (Anbieter, Suchender, Bearbeiter) - \item Moderator: optionale Kontrollkomponente - \begin{itemize} - \item delegiert Arbeit nach bestimmter Strategie an Spezialisten - - \end{itemize} - \end{itemize} + \begin{itemize*} + \item Spezialist (Worker): aktiver Nutzer (Anbieter, Suchender, Bearbeiter) + \item Moderator: optionale Kontrollkomponente + \begin{itemize*} + \item delegiert Arbeit nach bestimmter Strategie an Spezialisten + + \end{itemize*} + \end{itemize*} \item Datenmodell: - \begin{itemize} - \item globaler virtueller persistenter Speicher - \item allgemein: Tupel \textbf{$$} - \item Methoden zum Lesen, Schreiben, Aktualisieren, Löschen - \end{itemize} + \begin{itemize*} + \item globaler virtueller persistenter Speicher + \item allgemein: Tupel \textbf{$$} + \item Methoden zum Lesen, Schreiben, Aktualisieren, Löschen + \end{itemize*} \item Fehler- und Terminierungssemantiken: - \begin{itemize} - \item als verlässliche und unverlässliche Variante umsetzbar - \item Kommunikationsoperationen i.d.R asynchron - \end{itemize} -\end{itemize} + \begin{itemize*} + \item als verlässliche und unverlässliche Variante umsetzbar + \item Kommunikationsoperationen i.d.R asynchron + \end{itemize*} +\end{itemize*} \subsubsection{Blackboards: Vor- und Nachteile} -\begin{itemize} +\begin{itemize*} \item Vorteile - \begin{itemize} - \item Offenheit: neue Spezialistentypen möglich - \item gute Lastskalierbarkeit: mehr Spezialisten hinzufügen - \item Interoperabilität durch gemeinsame Tupeldefinition - \item Anonymität + Kommunikationskontrolle - \item Fehlertoleranz: redundante Spezialisten - \item Nutzung von Nebenläufigkeit für Spezialisten - \end{itemize} + \begin{itemize*} + \item Offenheit: neue Spezialistentypen möglich + \item gute Lastskalierbarkeit: mehr Spezialisten hinzufügen + \item Interoperabilität durch gemeinsame Tupeldefinition + \item Anonymität + Kommunikationskontrolle + \item Fehlertoleranz: redundante Spezialisten + \item Nutzung von Nebenläufigkeit für Spezialisten + \end{itemize*} \item Nachteile - \begin{itemize} - \item Synchronisation der Schreibzugriffe am Board - \item Moderator potentieller Engpass - \item Board und Moderator sind potentielle Single Point of Failures - \item erschwerte Testbarkeit durch Asynchronität, Nichtdeterminismus - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Synchronisation der Schreibzugriffe am Board + \item Moderator potentieller Engpass + \item Board und Moderator sind potentielle Single Point of Failures + \item erschwerte Testbarkeit durch Asynchronität, Nichtdeterminismus + \end{itemize*} +\end{itemize*} \subsubsection{Ereignisbasierte Modelle} -\begin{itemize} +\begin{itemize*} \item Asynchronität von Blackboards nicht immer hilfreich - \begin{itemize} - \item Niemand weiß, wann etwas an das Board gepinnt wird - \item warten auf bestimmte Tupel (nur Angebote von Mountainbikes) ist umständlich - \end{itemize} + \begin{itemize*} + \item Niemand weiß, wann etwas an das Board gepinnt wird + \item warten auf bestimmte Tupel (nur Angebote von Mountainbikes) ist umständlich + \end{itemize*} \item Börsen, Nachrichtenagenturen, Replikationssysteme sind an bestimmten Themen/Ereignissen interessiert \item Ereignisbasierte Modelle: - \begin{itemize} - \item nutzen ebenfalls autonome und anonyme Komponenten - \item verfügen zusätzlich über asynchrone Benachrichtigung über Veränderung - \end{itemize} -\end{itemize} + \begin{itemize*} + \item nutzen ebenfalls autonome und anonyme Komponenten + \item verfügen zusätzlich über asynchrone Benachrichtigung über Veränderung + \end{itemize*} +\end{itemize*} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-060} \end{center} \subsubsection{Ereignisbasierte Modelle: Modell-Sicht} -\begin{itemize} +\begin{itemize*} \item Rollenmodell: - \begin{itemize} - \item Herausgeber (Publisher): registriert Abonnenten und ihre Interesse an bestimmten Themen, meldet Ereignisse an Abonnenten - \item Abonnent (Subscriber): abonniert Themen bei Herausgebern, erhält passende Ereignisse zugeteilt - \item Agentur: optionale Abonnementverwaltung, Anonymisierung, zeitliche und räumliche Entkopplung - \end{itemize} + \begin{itemize*} + \item Herausgeber (Publisher): registriert Abonnenten und ihre Interesse an bestimmten Themen, meldet Ereignisse an Abonnenten + \item Abonnent (Subscriber): abonniert Themen bei Herausgebern, erhält passende Ereignisse zugeteilt + \item Agentur: optionale Abonnementverwaltung, Anonymisierung, zeitliche und räumliche Entkopplung + \end{itemize*} \item Datenmodell: - \begin{itemize} - \item allgemein: Tupel, z.B. $$ - \item aber auch problemspezifischer Ausprägungen - \item Methoden: Melden (notify/publish), Abonnieren (subscribe/unsubscribe) - \item Ankündigen/Aktualisieren von Ereignisreportoires (advertise/unadvertise) - \end{itemize} + \begin{itemize*} + \item allgemein: Tupel, z.B. $$ + \item aber auch problemspezifischer Ausprägungen + \item Methoden: Melden (notify/publish), Abonnieren (subscribe/unsubscribe) + \item Ankündigen/Aktualisieren von Ereignisreportoires (advertise/unadvertise) + \end{itemize*} \item Fehler- und Terminierungssemantiken: - \begin{itemize} - \item Ankündigungen/Aktualisieren i.d.R. verlässlich, daher synchron - \item Melden unverlässlich, da Abonnenten ausgefallen sein können, daher asynchron - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Ankündigungen/Aktualisieren i.d.R. verlässlich, daher synchron + \item Melden unverlässlich, da Abonnenten ausgefallen sein können, daher asynchron + \end{itemize*} +\end{itemize*} \subsubsection{Ereignisbasierte Modelle: Vor- und Nachteile} -\begin{itemize} +\begin{itemize*} \item Vorteile: - \begin{itemize} - \item wie Blackboards, plus - \item direkte Benachrichtigungen bei gesuchten Ereignissen/Themen - \end{itemize} + \begin{itemize*} + \item wie Blackboards, plus + \item direkte Benachrichtigungen bei gesuchten Ereignissen/Themen + \end{itemize*} \item Nachteile: - \begin{itemize} - \item erhebliche Management-Last beim Vermittler - \begin{itemize} - \item potentieller Engpass, SPoF - es sei denn… - \end{itemize} - \end{itemize} - Variante: mehrere Vermittler-Instanzen parallel und verteilt in einer Clusterumgebung - \begin{itemize} - \item RabbitMQ, Apache Kafka, Apache ActiveMQ - \end{itemize} -\end{itemize} + \begin{itemize*} + \item erhebliche Management-Last beim Vermittler + \begin{itemize*} + \item potentieller Engpass, SPoF - es sei denn… + \end{itemize*} + \end{itemize*} + Variante: mehrere Vermittler-Instanzen parallel und verteilt in einer Clusterumgebung + \begin{itemize*} + \item RabbitMQ, Apache Kafka, Apache ActiveMQ + \end{itemize*} +\end{itemize*} \subsubsection{Beispiel: RabbitMQ} RabbitMQ -\begin{itemize} +\begin{itemize*} \item ist ein Message-Broker, dh. Vermittlersystem für Nachrichten zwischen Publishern und Subscriber \item implementiert in Erlang - \begin{itemize} - \item Topic ist eine Liste von Wörtern, durch Punkte getrennt - \item Wildcards möglich, * ersetzt genau ein Wort, \# ersetzt mehrere Wörter - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Topic ist eine Liste von Wörtern, durch Punkte getrennt + \item Wildcards möglich, * ersetzt genau ein Wort, \# ersetzt mehrere Wörter + \end{itemize*} +\end{itemize*} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-061} @@ -7283,77 +7133,77 @@ RabbitMQ \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-062} \end{center} -\begin{itemize} +\begin{itemize*} \item Zeilen 7,8: \textbf{ConnectionFactory} zum Handling von Verbindungen, im Beispiel nur auf dem lokalen Host \item Zeilen 9,10: erstelle neue Verbindungen und einen Channel \item Zeile 11: Nachrichten sollen anhand des Topics zugestellt werden \item Zeile 13: veröffentliche eine Nachricht: sende an den Exchange "rmq\_test" eine Nachricht mit dem Topic \textbf{uni.ilm.dbis} und dem Inhalt "Hallo Welt". - \begin{itemize} - \item \textbf{null} hier für eventuelle weitere Einstellungen - \end{itemize} -\end{itemize} + \begin{itemize*} + \item \textbf{null} hier für eventuelle weitere Einstellungen + \end{itemize*} +\end{itemize*} \subsubsection{RabbitMQ Subscribe} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-063} \end{center} -\begin{itemize} +\begin{itemize*} \item Zeilen 1\&2: Connection und Channel erstellen, siehe vorherige Bilder \item Zeilen 4-6: Queue erzeugen und auf Topics registrieren \item Zeilen 8-13: Java-Lambda Funktion anlegen, wird für jede eintreffende Nachricht aufgerufen \item Zeilen 15\&16: Warte auf der erzeugten Queue auf Nachrichten - \begin{itemize} - \item durch \textbf{true} Parameter wird ankommende Nachricht mit \textbf{ACK} quittiert - \item zweite anonyme Funktion für Handling von Abbrüchen - \end{itemize} -\end{itemize} + \begin{itemize*} + \item durch \textbf{true} Parameter wird ankommende Nachricht mit \textbf{ACK} quittiert + \item zweite anonyme Funktion für Handling von Abbrüchen + \end{itemize*} +\end{itemize*} \subsection{Cloud Computing} \subsubsection{Cloud Computing} -\begin{itemize} +\begin{itemize*} \item Verteilte Systeme benötigen oftmals viele Knoten \item Administration der Systeme erfordert viel Aufwand \item Hardware, Betriebssystem und Anwendungssoftware veralten schnell \item hohe Kosten, aber Systeme oftmals nicht voll ausgelastet \item Grundidee: einmal eingerichtete Hardware, durch Virtualisierung mehreren Kunden zugänglich machen \item Zugriff über das Internet -\end{itemize} +\end{itemize*} \subsubsection{Cloud Computing: Arten und Ziele} \noindent Arten von Clouds -\begin{itemize} +\begin{itemize*} \item Public Cloud: für jeden zugängliche Ressourcen \item Private Cloud: z.B. in Unternehmen im eigenen Netzwerk \item Community Cloud: Cloud-Umgebung wird von mehreren (festgelegten) Gruppen/Organisationen geteilt \item Hybrid Cloud: private Clouds, aber für Skalierbarkeit auch Ressourcen von Public Clouds nutzen -\end{itemize} +\end{itemize*} Ziele -\begin{itemize} +\begin{itemize*} \item Auslastung der physischen Hardware durch Virtualisierung $\Rightarrow$ Umsatzsteigerung \item für Kunden - \begin{itemize} - \item hohe Verfügbarkeit, Skalierbarkeit - \item keine Anschaffungs-, geringere Administrationskosten - \end{itemize} -\end{itemize} + \begin{itemize*} + \item hohe Verfügbarkeit, Skalierbarkeit + \item keine Anschaffungs-, geringere Administrationskosten + \end{itemize*} +\end{itemize*} \subsubsection{Cloud Computing: Geschätsmodell} Cloud Computing als Geschäftsmodell: -\begin{itemize} +\begin{itemize*} \item Cloudprovider stellen Ressourcen bereit: - \begin{itemize} - \item persistente Speicher (HDDs, SSDs) - \item CPUs/RAM - \item spezialisierte Hardware (FPGAs, GPUs) - \item Software - \end{itemize} + \begin{itemize*} + \item persistente Speicher (HDDs, SSDs) + \item CPUs/RAM + \item spezialisierte Hardware (FPGAs, GPUs) + \item Software + \end{itemize*} \item verschiedene Maschinenvarianten - \begin{itemize} - \item Datenverarbeitungsoptimiert, - Arbeitsspeicheroptimiert, uvm. - \end{itemize} + \begin{itemize*} + \item Datenverarbeitungsoptimiert, + Arbeitsspeicheroptimiert, uvm. + \end{itemize*} \item Kunden starten dynamisch Instanzen der Maschinen/Software - \begin{itemize} - \item Bezahlung nur für genutzte Zeit ("pay-as-you-go") - \end{itemize} -\end{itemize}\clearpage + \begin{itemize*} + \item Bezahlung nur für genutzte Zeit ("pay-as-you-go") + \end{itemize*} +\end{itemize*}\clearpage \subsubsection{Cloud Computing: Architekturen} \begin{center} \centering @@ -7367,44 +7217,44 @@ Cloud Computing als Geschäftsmodell: Hinweis: Bei AWS, Google, Azure viele Dienste auch (dauerhaft) kostenlos nutzbar! \subsubsection{Microservices} -\begin{itemize} +\begin{itemize*} \item typische Anwendung hat verschiedene Komponenten (Suche, Buchungen/Warenkorb, Bezahlsystem, Bewertungssystem) \item monolithisch: alle Komponenten in einer Anwendung - \begin{itemize} - \item bei Bugfix/Update gesamte Anwendung aktualisieren und neu starten - \end{itemize} + \begin{itemize*} + \item bei Bugfix/Update gesamte Anwendung aktualisieren und neu starten + \end{itemize*} \item Ansatz Microservice: jede Komponente oder Funktionalität einer Anwendung als unabhängigen Dienst, läuft 24/7 \item Dienste mit unterschiedlichen Sprachen und Technologien umsetzbar \item unterstützt durch Virtualisierung und Cloud-Angebote, z.B. Serverless Computing -\end{itemize} +\end{itemize*} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-066} \end{center} \subsubsection{Serverless Computing} -\begin{itemize} +\begin{itemize*} \item wörtlich: "ohne Server" - \begin{itemize} - \item aber in Wirklichkeit: "es ist nicht dein Server" - \item Idee: Entwickler konzentriert sich auf (kleine Funktion) - \begin{itemize} - \item Function-as-a-Service (FaaS) - \end{itemize} - \item Laufzeitumgebung bzw. Cloud-Anbieter stellen Server und Konfiguration bereit - \item Ausführung der Funktion nach Bedarf - \item verschiedene Programmiersprachen unterstützt - \end{itemize} -\end{itemize} + \begin{itemize*} + \item aber in Wirklichkeit: "es ist nicht dein Server" + \item Idee: Entwickler konzentriert sich auf (kleine Funktion) + \begin{itemize*} + \item Function-as-a-Service (FaaS) + \end{itemize*} + \item Laufzeitumgebung bzw. Cloud-Anbieter stellen Server und Konfiguration bereit + \item Ausführung der Funktion nach Bedarf + \item verschiedene Programmiersprachen unterstützt + \end{itemize*} +\end{itemize*} Aufgaben des Entwicklers -\begin{itemize} +\begin{itemize*} \item schreibt nur auszuführenden Code \item konfiguriert Triggerereignisse - \begin{itemize} - \item REST Aufruf, neuer Eintrag in DB, Monitoring-Ereignis einer anderen Anwendung - \item $\Rightarrow$ Funktion läuft nur nach Eintreten des Triggers - \item optional: verknüpft z.B. weitere Funktion, die nach Beendigung ausgeführt wird - \end{itemize} -\end{itemize} + \begin{itemize*} + \item REST Aufruf, neuer Eintrag in DB, Monitoring-Ereignis einer anderen Anwendung + \item $\Rightarrow$ Funktion läuft nur nach Eintreten des Triggers + \item optional: verknüpft z.B. weitere Funktion, die nach Beendigung ausgeführt wird + \end{itemize*} +\end{itemize*} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-067} @@ -7412,41 +7262,41 @@ Aufgaben des Entwicklers AWS Lambda Designer: HTTP Trigger, auszuführende Funktion und Nachfolgefunktion. \subsubsection{Vergleich Microservice vs. Serverless} Micorservice: -\begin{itemize} +\begin{itemize*} \item Anwendung wird durch Dienste strukturiert \item Dienst hat einen Service Contract (Schnittstellendefinition) - \begin{itemize} - \item z.B. Portnummer, Antwortzeitgarantien, - \item unterstütze Anfragen und resultierende Antworten - \end{itemize} + \begin{itemize*} + \item z.B. Portnummer, Antwortzeitgarantien, + \item unterstütze Anfragen und resultierende Antworten + \end{itemize*} \item läuft kontinuierlich in einem eigenen (virtuellen) Knoten -\end{itemize} +\end{itemize*} Serverless -\begin{itemize} +\begin{itemize*} \item einzelne Funktionen; kleiner als ein ganzer Dienst \item wird nur nach Trigger ausgeführt \item kurze Lebenszeit; oft begrenzt durch den Anbieter -\end{itemize} +\end{itemize*} Mittlerweile auch Serverless Microservice: Microservice nicht immer ausführen, sondern nach Trigger \subsubsection{AWS Lambda: Java Beispiel} \begin{center} \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-068} \end{center} -\begin{itemize} +\begin{itemize*} \item \textbf{RequestHandler} als Einstiegspunkt für Lambda-Funktionen \item \textbf{handleRequest} wird von der Lambda-Umgebung aufgerufen -\end{itemize} +\end{itemize*} \subsubsection{Spring Functions} -\begin{itemize} +\begin{itemize*} \item neben AWS viele weitere Functions-Angebote \item Anbieter-spezifische API macht Migration schwierig \item zusätzliche Frameworks mit Plugins für konkrete Anbieter - \begin{itemize} - \item serverless.com - \item Spring Cloud Functions - \end{itemize} -\end{itemize} + \begin{itemize*} + \item serverless.com + \item Spring Cloud Functions + \end{itemize*} +\end{itemize*} \subsubsection{Spring Cloud Function: Projekt anlegen} \begin{center} \centering @@ -7458,36 +7308,36 @@ Projekt generieren, herunterladen und entpacken \centering \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-070} \end{center} -\begin{itemize} +\begin{itemize*} \item Zeile 1: für Spring Boot, diese Klasse enthält Definitionen für Dienste \item Zeilen 3-5: Ausführen der Klasse als Spring Applikation \item Zeile 7: Ergebnis der Methode als Bean behandeln - \begin{itemize} - \item Bean Objekt wird von Spring verwaltet, als serverless Funktion - \item durch \textbf{Spring Web} Funktionsname = REST Pfad - \end{itemize} + \begin{itemize*} + \item Bean Objekt wird von Spring verwaltet, als serverless Funktion + \item durch \textbf{Spring Web} Funktionsname = REST Pfad + \end{itemize*} \item Zeile 8ff: Methode \textbf{info} gibt eine Java Lambda-Funktion zurück - \begin{itemize} - \item Eingabe vom Typ \textbf{String} - \item Ergebnis vom Typ \textbf{FileInfo} - \end{itemize} -\end{itemize} + \begin{itemize*} + \item Eingabe vom Typ \textbf{String} + \item Ergebnis vom Typ \textbf{FileInfo} + \end{itemize*} +\end{itemize*} \subsubsection{Aufruf der Spring Cloud Funktion} -\begin{enumerate} +\begin{enumerate*} \item Projekt kompilieren \newline ./mvnw clean install \item Starten \newline - java - jar target/fileinfo-0.0.1-SNAPSHOT.jar + java - jar target/fileinfo-0.0.1-SNAPSHOT.jar \item Aufruf als normaler REST call - \newline curl localhost:8080/info -d myfile.txt - \newline Antwort - \newline \{"name":"myfile.txt","size":1234\} -\end{enumerate} + \newline curl localhost:8080/info -d myfile.txt + \newline Antwort + \newline \{"name":"myfile.txt","size":1234\} +\end{enumerate*} \subsubsection{Zusammenfassung} -\begin{itemize} +\begin{itemize*} \item Cloud-Angebote auf verschiedenen Ebenen (IaaS, PaaS, SaaS) \item Cloud \& Virtualisierung verringern Anschaffungs- und Administrationskosten \item Microservices: monolithische Anwendung in kleinere Dienste zerlegen \item Serverless: nur noch Funktion implementieren (FaaS) -\end{itemize} +\end{itemize*} \end{document} \ No newline at end of file