Eine Aufgabe, unzählige Teile
Ein Foto aufzuhellen heißt, dieselbe kleine Zahl zu Millionen von Pixeln zu addieren. Ein Pixel hängt nicht von einem anderen ab, also können tausende kleine Arbeiter jeweils ein paar übernehmen. Eine Grafikkarte ist genau dafür gebaut. In diesem Lab gibt es keine Grafikkarte, aber du kannst die Ideen sehen, auf denen sie beruht.
Am Ende dieser Lektion
Notiz
Ich kann erklären, worin sich dieselbe Aufgabe auf vielen Daten gleichzeitig von einer Schleife unterscheidet und warum mehr Arbeiter eine Aufgabe nicht im gleichen Maß schneller machen.
Schlüsselwörter
- Parallel: mehrere Teile einer Aufgabe, die gleichzeitig erledigt werden.
- Worker (Arbeiter): ein Prozess oder Thread, der einen Teil der Aufgabe erledigt.
- Serieller Anteil: der Teil einer Aufgabe, der sich nicht aufteilen lässt und der Reihe nach erledigt werden muss.
- Beschleunigung: wie viel mal schneller eine Aufgabe ist als mit einem einzigen Worker.
- Thread: ein kleiner Arbeiter; eine GPU lässt tausende gleichzeitig laufen.
- Kernel: die kleine Funktion, die jeder Thread ausführt, jeder für sein eigenes Element.
Zum Einstieg: was du schon weißt
Notiz
Antworte im Kopf. 1. Was macht eine for-Schleife? 2. Wenn eine Person 8 Minuten braucht, um 80 Flyer zu falten, wie lange brauchen 4 Personen, wenn sie den Stapel gleichmäßig teilen? 3. Und wenn eine der 8 Minuten für die Adressliste draufgeht, die nur eine Person schreiben kann? Antworten: Sie wiederholt dieselben Schritte nacheinander; 2 Minuten; mehr als 2, weil die Adressliste für eine einzelne Person weiter 1 Minute dauert.
Bevor du anfängst
Notiz
Klicke oben in der Leiste dieser Lektion auf Lab starten und warte, bis dort steht, dass es läuft. Es gibt einen Container, jupyter, und das Terminal erreicht sein Python mit docker exec jupyter, das einen Befehl darin ausführt. Der Link JupyterLab öffnet seine Webseite, Port 8888. Hier gibt es keine GPU. Jede Schaltfläche Ausführen unten tippt ihren Befehl ins Terminal deines Labs, einen nach dem anderen in derselben Sitzung. Wenn du zuschauen oder eigene Befehle tippen willst, klicke in der Leiste auf Terminal öffnen (neuer Tab).
Ein Parallelprojekt ist ein Ordner mit kleinen Skripten
ls listet den Arbeitsordner auf: das Notebook start.ipynb und den Ordner steps. cat zeigt das erste Skript. Es addiert die Quadrate von 1 bis 1000 zweimal: einmal mit einer Python-Schleife und einmal mit einer einzigen NumPy-Operation, die dieselbe Arbeit auf dem ganzen Array auf einmal erledigt.
docker exec jupyter lsHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
docker exec jupyter cat steps/vectors.pyHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Eine Operation auf einem ganzen Array
Beide Wege ergeben 333833500, und das Skript meldet, dass die beiden Ergebnisse gleich sind. Die Schleife braucht 2000 Operationen auf Python-Ebene, eine Multiplikation und eine Addition für jede der 1000 Zahlen. NumPy braucht 1, weil ein einziger Aufruf die ganze Arbeit in schnellem, kompiliertem Code erledigt. Es wird keine Zeit gemessen, deshalb gibt das Skript jedes Mal dasselbe aus. Eine GPU geht weiter: eine Anweisung, tausende Elemente.
docker exec jupyter python3 steps/vectors.pyHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Eine Aufgabe auf Worker verteilen
Die Zahlen von 1 bis 40 werden in 4 Stücke zu je 10 zerlegt, und zwei Worker teilen sie sich. Jedes Stück ergibt eine Teilsumme, 55, 155, 255 und 355, immer in der Reihenfolge der Stücke aufgelistet, und zusammen ergeben sie 820, genau wie die Summe der ganzen Liste in einem Stück. Das Aufteilen funktioniert, weil sich jedes Stück addieren lässt, ohne die anderen zu kennen.
docker exec jupyter python3 steps/chunks.pyHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Die Grenze der Beschleunigung
Ein Teil jeder Aufgabe lässt sich nicht aufteilen. Die Tabelle berechnet die Beschleunigung mit 1, 2, 4 und 8 Workern, wenn sich 10, 25 und 50 Prozent nicht aufteilen lassen. Bei 10 Prozent seriellem Anteil bringen 8 Worker das 4,71-Fache, nicht das 8-Fache, und selbst unendlich viele Worker bringen höchstens das 10,0-Fache. Bei 50 Prozent seriellem Anteil bringen 8 Worker nur das 1,78-Fache.
docker exec jupyter python3 steps/amdahl.pyHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Prüfe dich
Tipp
Wie viel schneller ist die Aufgabe in der Tabelle mit 8 Workern, wenn sich 25 Prozent nicht aufteilen lassen? Antwort: das 2,91-Fache, nicht das 8-Fache, weil das serielle Viertel weiter von einem einzigen Worker erledigt wird.
Ein Start von Blöcken aus Threads
Eine GPU führt eine Funktion namens Kernel einmal für jeden Thread aus. Die Threads sind in Blöcken zusammengefasst, und jeder berechnet mit Blocknummer * Blockgröße + Threadnummer, welches Element seines ist. Dieses Skript simuliert den Start von 3 Blöcken zu je 4 Threads über 10 Elemente, um zwei Vektoren zu addieren. Es gibt den globalen Index jedes Threads von 0 bis 11 aus und das Ergebnis 11, 22 und so weiter bis 110. Die letzten beiden Threads, 10 und 11, liegen hinter dem Ende und tun nichts.
docker exec jupyter python3 steps/kernel.pyHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Daten zu bewegen kostet auch Zeit
Ein Gerät hat zwei Grenzen: wie schnell es Bytes bewegt und wie schnell es rechnet. Dieses Skript ist reine Rechnerei mit einem erfundenen Gerät, das 500 Gigabyte pro Sekunde bewegt und 10 Teraflops pro Sekunde schafft. Eine Vektoraddition mit 10 Millionen Elementen bewegt 120000000 Bytes für 10000000 Rechenschritte, sie wird also vom Speicher begrenzt. Eine Matrixmultiplikation von 1000 mal 1000 bewegt 12000000 Bytes für 2000000000 Rechenschritte, sie wird also von der Rechenleistung begrenzt.
docker exec jupyter python3 steps/transfer.pyHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Ein häufiger Fehler
Warnung
Viele denken, 8 Worker machten eine Aufgabe 8-mal so schnell. Das stimmt nur, wenn nichts an der Aufgabe der Reihe nach erledigt werden muss. Was seriell bleibt, setzt eine Obergrenze, und das Bewegen der Daten kann mehr kosten als das Rechnen. Stelle dir zwei Fragen, bevor du Worker hinzufügst: Wie viel lässt sich nicht aufteilen, und wartet die Aufgabe auf den Speicher oder auf die Rechenleistung?
Dieselbe Arbeit im Notebook
Öffne JupyterLab in der Leiste und doppelklicke auf start.ipynb. Jede Zelle führt ein Skript aus, du siehst also, was die sechs Skripte getan haben, eine Idee nach der anderen. Klicke auf eine Zelle und drücke Umschalt und Eingabe, um sie auszuführen. Zum Erkunden öffnest du ein Skript in der Dateiliste, änderst eine Zahl, zum Beispiel die Anzahl der Stücke oder den Anteil, der sich nicht aufteilen lässt, und führst es erneut aus.
Abschlussprüfung
Tipp
Antworte, ohne zurückzuschauen. 1. Wie viele Operationen auf Python-Ebene brauchte die Schleife für die Quadratsumme, und wie viele brauchte NumPy? 2. Auf welche Summe kamen die vier Teilsummen? 3. Was ist bei 10 Prozent seriellem Anteil die größte Beschleunigung, die irgendeine Anzahl von Workern bringen kann? 4. Was berechnet jeder Thread in einem Kernel zuerst? Antworten: 2000 und 1; 820; das 10,0-Fache; welches Element seines ist, also seinen globalen Index.
Für Lehrkräfte
Tipp
Diese Lektion ist ein kleines Beispiel für eine Lektion, die in einem Lab abläuft: Die Schritte und die Schaltflächen „Ausführen“ sind die Lektion, und das Lab ist die eigene Kopie jeder lernenden Person. Das Lab ist ein Dienst, JupyterLab mit NumPy, und hat keine GPU: Es vermittelt das Modell, auf dem eine GPU gebaut ist, und nichts hier bewertet sich selbst. Die Lernenden geben eine kurze eigene Änderung an einem Skript ab und einen Satz dazu, was sich bewegt hat. Kernpunkte: 1. Eine Operation auf einem ganzen Array ersetzt eine Schleife. 2. Eine auf Worker verteilte Aufgabe behält einen seriellen Anteil, der eine Obergrenze setzt. 3. Ein Kernel läuft einmal pro Thread, und jeder Thread berechnet sein eigenes Element. Häufiger Fehler: glauben, 8 Worker brächten die 8-fache Geschwindigkeit. Material: das Lab und ein Browser. Tipp: Lassen Sie die Lernenden jeden Wert der Tabelle vorhersagen, bevor sie auf „Ausführen“ klicken. Jedes Skript gibt jedes Mal dasselbe aus, weil nichts gemessen wird. Kopieren Sie sie in ein eigenes Modul und passen Sie sie an, oder erstellen Sie eine neu mit dem Menü Lab des Lektions-Editors.