CUDA-Runtime

Wenn eine Anwendung Berechnungen auf einer Grafikkarte ausführen will, braucht sie eine Vermittlungsschicht: Software, die Speicher reserviert, Rechenaufträge startet und Daten zwischen Hauptprozessor und Grafikkarte transportiert. Diese Vermittlungsschicht heißt CUDA-Runtime.

Die CUDA-Runtime ist eine Laufzeitbibliothek von NVIDIA. Sie bildet die Schnittstelle zwischen GPU-beschleunigten Anwendungen und dem Grafiktreiber. Jedes Mal, wenn ein Deep-Learning-Framework einen Tensor auf die GPU verschiebt oder eine Matrixmultiplikation startet, ruft es Funktionen der CUDA-Runtime auf.

Was die CUDA-Runtime zwischen Anwendung und Treiber vermittelt

Die CUDA-Runtime abstrahiert die Kommunikation mit dem Grafiktreiber. Ohne sie müsste jede Anwendung direkt mit dem Treiber sprechen, was komplizierte Low-Level-Aufrufe erfordert. Die Runtime stellt stattdessen eine einheitliche API bereit, die drei Kernaufgaben übernimmt: Speicherverwaltung auf der GPU, Start und Koordination von Rechenkernen (Kernels) und Datentransfer zwischen Hauptspeicher und VRAM.

Beispiel: Ein PyTorch-Programm enthält die Zeile tensor.cuda(). Diese Anweisung löst intern einen Aufruf der CUDA-Runtime-Funktion cudaMalloc aus, die Speicher auf der GPU reserviert. Anschließend kopiert cudaMemcpy die Daten vom Hauptspeicher in den GPU-Speicher.

Beispiel: Bei einer Matrixmultiplikation auf der GPU ruft PyTorch intern cudaLaunchKernel auf. Die Runtime übersetzt diesen Aufruf in Befehle, die der Treiber an die GPU weiterleitet. Die Anwendung selbst hat keinen direkten Kontakt zum Treiber.

Fachliche Einordnung: Die CUDA-Runtime gehört zur User-Space-Schicht des CUDA-Stacks. Sie kommuniziert über den CUDA-Driver mit dem Kernel-Mode-Treiber. Diese Trennung ermöglicht es, Runtime-Versionen unabhängig vom Treiber zu aktualisieren, solange die Versionskompatibilität eingehalten wird.

Zwei Abstraktionsebenen: Runtime-API und Driver-API

NVIDIA stellt zwei APIs bereit, die unterschiedliche Abstraktionsebenen bedienen. Die Runtime-API (libcudart) automatisiert die GPU-Verwaltung weitgehend. Die Driver-API (libcuda) bietet volle Kontrolle, erfordert aber deutlich mehr Code. Beide APIs kommunizieren mit demselben Treiber.

Beispiel: Um einen Rechenkern mit der Runtime-API zu starten, genügt ein Aufruf von cudaLaunchKernel mit wenigen Parametern. Dieselbe Operation über die Driver-API erfordert zusätzlich das manuelle Laden des Moduls, das Auffinden der Funktion im Modul und die explizite Konfiguration des Ausführungskontexts.

Beispiel: PyTorch nutzt intern die Runtime-API. Spezialisierte CUDA-Bibliotheken wie cuBLAS verwenden ebenfalls die Runtime-API. Nur Anwendungen, die maximale Kontrolle über GPU-Kontexte benötigen (etwa Virtualisierungslösungen), greifen auf die Driver-API zurück.

Wie die Runtime GPU-Speicher verwaltet

Die Speicherverwaltung ist eine der zentralen Aufgaben der CUDA-Runtime. Sie stellt Funktionen bereit, um Speicher auf der GPU zu reservieren (cudaMalloc), freizugeben (cudaFree) und zwischen Host und Device zu kopieren (cudaMemcpy). Seit CUDA 6.0 gibt es zusätzlich Unified Memory (cudaMallocManaged), das die Trennung zwischen Host- und Device-Speicher auf Programmierebene aufhebt.

Beispiel: Beim Training eines Transformer-Modells mit Batch-Size 32 reserviert die Runtime für jeden Tensor im Forward-Pass GPU-Speicher. Reicht der VRAM nicht aus, liefert die Runtime den Fehlercode cudaErrorMemoryAllocation. Frameworks wie PyTorch fangen diesen Fehler ab und geben die Meldung "CUDA out of memory" aus.

Beispiel: Unified Memory vereinfacht die Programmierung, weil der Speicher automatisch zwischen Host und GPU migriert wird. In der Praxis ist die Leistung allerdings geringer als bei explizitem Speichermanagement, weil die automatische Migration über den PCIe-Bus Wartezeiten erzeugt.

Parallele Ausführung mit CUDA-Streams

Die CUDA-Runtime ermöglicht parallele Ausführung über sogenannte Streams. Ein Stream ist eine Warteschlange von GPU-Operationen, die nacheinander ausgeführt werden. Mehrere Streams laufen jedoch gleichzeitig. Das erlaubt es, Datentransfers und Berechnungen zu überlappen.

AnwendungPyTorch, TensorFlow
Stream 1Berechnung
Stream 2Datentransfer
Stream 3Berechnung
CUDA-RuntimeScheduling und Synchronisation

Beispiel: Während Stream 1 eine Matrixmultiplikation auf der GPU ausführt, kopiert Stream 2 bereits den nächsten Batch vom Hauptspeicher in den GPU-Speicher. Ohne Streams müsste die GPU nach jeder Berechnung warten, bis der nächste Datentransfer abgeschlossen ist.

Beispiel: PyTorch nutzt standardmäßig einen Default-Stream pro GPU. Für fortgeschrittene Optimierungen stellt torch.cuda.Stream() zusätzliche Streams bereit. Das manuelle Stream-Management erfordert sorgfältige Synchronisation, weil Operationen in verschiedenen Streams keine Reihenfolgegarantie haben.

Versionskompatibilität und Fehlerquellen

Die CUDA-Runtime trägt eine Versionsnummer, die zur CUDA-Version passen muss, mit der eine Anwendung kompiliert wurde. Stimmt die Version nicht überein, schlägt die Initialisierung fehl. Seit CUDA 11.0 gibt es Minor Version Compatibility: Eine Anwendung, die mit CUDA 11.2 kompiliert wurde, funktioniert auch mit der Runtime aus CUDA 11.8, solange die Hauptversion (11) übereinstimmt.

Beispiel: Ein häufiger Fehler in ML-Umgebungen ist CUDA error: no kernel image is available for execution on the device. Dieser Fehler entsteht, wenn die CUDA-Runtime für eine ältere GPU-Architektur kompiliert wurde als die tatsächlich verbaute Karte. Die Lösung besteht darin, PyTorch mit der passenden CUDA-Version neu zu installieren.

Beispiel: Der Befehl nvcc --version zeigt die Version des CUDA-Compilers und damit die installierte Toolkit-Version. Die tatsächlich geladene Runtime-Version lässt sich in Python mit torch.version.cuda prüfen. Beide Angaben können voneinander abweichen, wenn mehrere CUDA-Installationen auf einem System existieren.

CUDA-Runtime in der ML-Praxis

In Machine-Learning-Workflows ist die CUDA-Runtime allgegenwärtig, auch wenn Anwender sie selten direkt aufrufen. Frameworks wie PyTorch und TensorFlow kapseln die Runtime-Aufrufe vollständig. Die Runtime wird erst sichtbar, wenn Fehler auftreten oder Leistungsengpässe analysiert werden.

Beispiel: Beim Profiling eines Trainingslaufs mit torch.cuda.Event misst die Runtime die exakte Ausführungszeit einzelner GPU-Operationen. Das zeigt, ob der Engpass bei der Berechnung, beim Datentransfer oder bei der Synchronisation liegt.

Beispiel: Bei Quantisierung von Modellgewichten (etwa von 32-Bit auf 8-Bit) verändert sich das Speicherzugriffsverhalten auf der GPU. Die CUDA-Runtime muss weniger Speicher reservieren, und die Berechnungskerne arbeiten mit kompakteren Datentypen. Das reduziert sowohl den VRAM-Verbrauch als auch die Rechenzeit pro Batch.

Grenzen und Einordnung

Die CUDA-Runtime ist an NVIDIA-Hardware gebunden. Anwendungen, die CUDA-Aufrufe verwenden, laufen nicht auf GPUs anderer Hersteller. Alternativen wie OpenCL, ROCm (AMD) oder oneAPI (Intel) bieten ähnliche Funktionalität, haben aber in der ML-Praxis weniger Verbreitung und geringere Framework-Unterstützung.

Die Runtime selbst führt keine Berechnungen durch. Sie koordiniert lediglich den Zugriff auf GPU-Ressourcen. Die eigentlichen Berechnungen finden in spezialisierten Bibliotheken statt (cuBLAS für lineare Algebra, cuDNN für neuronale Netze, cuFFT für Fourier-Transformationen). Diese Bibliotheken bauen auf der Runtime auf.

Fachliche Einordnung: Der CUDA-Stack ist proprietär. Das erzeugt eine Abhängigkeit von einem einzelnen Hersteller (Vendor Lock-in). Projekte wie Triton (OpenAI) oder MLIR versuchen, eine herstellerunabhängige Abstraktionsschicht für GPU-Programmierung zu etablieren. Bislang bleibt CUDA in der Praxis die dominante Plattform für GPU-beschleunigtes Deep Learning.


Karl Kratz · 11.12.2025 (aktualisiert 03.04.2026)

Technologie Hardware GPU