Lässt es sich mit cremefarbenem Papier, einer Tönung, mehr Kontrast oder größerer Schrift besser lesen? Wählen Sie Ihre Farben und Ihre Schrift, einmalig für alle Seiten.

Kostenlose Lektion

Einen GPU-Kernel schreiben

Nach dieser Lektion: Ich kann einen Kernel für eine einfache Aufgabe schreiben und seinen Start in Python nachspielen.

Lektion, 65 Min.2 BefehleParallelverarbeitung-Lab: wie eine GPU denkt, ohne GPU, in JupyterLab

Ein Kernel beschreibt die Arbeit eines einzigen Threads

Ein Kernel ist die kleine Funktion, die jeder Thread einer GPU für sein eigenes Element ausführt. Man schreibt ihn für ein Element und startet ihn für alle.

Ein Lager addiert zu jedem Artikel die Lieferung. Der Kernel rechnet einen Artikel, der Start verteilt ihn auf so viele Threads, wie es Artikel gibt.

Am Ende dieser Lektion

Notiz

Ich kann einen Kernel für eine einfache Aufgabe schreiben und seinen Start in Python nachspielen.

Schlüsselwörter

BegriffWas er bedeutetBeispiel
Kerneldie Funktion, die jeder Thread für sein Element ausführt
Startdas Ausführen des Kernels in allen Threads des Gitters (Launch)
Schutzabfrageprüft, ob der Thread ein Element hatif i < n
Ergebnisfelddas Feld, in das jeder Thread seine Stelle schreibt
Globaler Indexdie Nummer des Elements, das ein Thread bearbeitet

Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.

Der Start läuft hier Thread für Thread

Wichtig

Auf einer GPU laufen die Threads gleichzeitig. Im Lab und in dieser Lektion spielen zwei Schleifen den Start nach: erst alle Blöcke, darin alle Threads, nacheinander. Das Ergebnis ist dasselbe.

Der Kernel rechnet genau ein Element

Der Kernel bekommt Blocknummer und Threadnummer. Er rechnet daraus seinen globalen Index und bearbeitet genau dieses Element.

Jeder Thread schreibt nur an seine eigene Stelle im Ergebnisfeld. Darum stören sich zwei Threads nie.

Ein Kernel und sein Start

Code: python
# Ein Lager addiert eine Lieferung auf den Bestand, ein Thread je Artikel.
bestand = [4, 8, 15, 16, 23]
lieferung = [1, 2, 3, 4, 5]
summe = [0, 0, 0, 0, 0]
n = len(bestand)
block_groesse = 2
bloecke = 3


def kernel(block_nr, thread_nr):
    i = block_nr * block_groesse + thread_nr
    # Schutz: Threads hinter dem Ende haben kein Element.
    if i < n:
        summe[i] = bestand[i] + lieferung[i]


for block_nr in range(bloecke):
    for thread_nr in range(block_groesse):
        kernel(block_nr, thread_nr)
print(summe)
# Setzen Sie bloecke auf 2 und starten Sie erneut: Was fehlt?

Die Schutzabfrage hält überzählige Threads draußen

Fünf Artikel, Blöcke zu zwei Threads: Drei Blöcke geben sechs Threads. Der sechste hat den Index 5, und es gibt nur die Stellen 0 bis 4.

Ohne die Abfrage if i < n würde dieser Thread über das Ende hinaus schreiben.

Sechs Threads für fünf Artikel

BlockThreadIndexWas der Thread tut
000rechnet Artikel 0
011rechnet Artikel 1
102rechnet Artikel 2
113rechnet Artikel 3
204rechnet Artikel 4
215tut nichts, es gibt keinen Artikel 5

Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.

Das Lab zeigt denselben Aufbau mit zehn Elementen

Der Befehl unten gibt den Quelltext des Lab-Skripts kernel.py aus. Sie finden darin eine Funktion kernel und zwei Schleifen, genau wie oben.

Die Namen im Skript sind englisch. Lesen Sie nur, wie es den Index rechnet und die Schutzabfrage stellt.

Den Quelltext des Lab-Starts lesen

Befehl
docker exec jupyter cat steps/kernel.py

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Blöcke für fünf Artikel, Blockgröße zwei

Befehl
docker exec jupyter python3 -c "print(-(-5 // 2))"

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Was ohne Schutzabfrage geschieht

Code: python
# Derselbe Start, aber der Kernel prueft sein Element nicht.
bestand = [4, 8, 15, 16, 23]
verdoppelt = [0, 0, 0, 0, 0]
block_groesse = 2

for block_nr in range(3):
    for thread_nr in range(block_groesse):
        i = block_nr * block_groesse + thread_nr
        verdoppelt[i] = bestand[i] * 2
print(verdoppelt)
# Der letzte Thread meldet einen Fehler. Fuegen Sie vor dem Schreiben
# die Zeile "if i < len(bestand):" ein und ruecken Sie die Zeile danach ein.

Ein häufiger Fehler

Warnung

Viele denken, ein Kernel laufe als Schleife über alle Elemente. Er beschreibt aber nur ein Element. Die Schleife ist der Start. Steht eine Schleife über die Daten im Kernel, macht jeder Thread die Arbeit aller.

Weiterführend

Ändern Sie den Kernel so, dass er jedes Element mit 3 multipliziert, statt zwei Felder zu addieren. Was bleibt gleich: der Index, die Schutzabfrage oder der Start?

Schreiben Sie auf, was sich ändert und was nicht.

Über dieses Lab und seinen Quellcode: Parallelverarbeitung-Lab: wie eine GPU denkt, ohne GPU, in JupyterLab

Sie unterrichten eine Gruppe? Weisen Sie dieses Modul Ihren Studierenden zu, mit Handbuch für Lehrende, automatisch geprüften Aufgaben und einem Lab für jede Person.