Wenn ich mir die Antworten und Kommentare zu CUDA-Fragen und im CUDA-Tag-Wiki ansehe , wird häufig empfohlen, den Rückgabestatus jedes API-Aufrufs auf Fehler zu überprüfen. Die API-Dokumentation enthält Funktionen wie cudaGetLastError
, cudaPeekAtLastError
und cudaGetErrorString
, aber wie lassen sich diese am besten zusammenstellen, um Fehler zuverlässig abzufangen und zu melden, ohne dass viel zusätzlicher Code erforderlich ist?
cuda
error-checking
Talonmien
quelle
quelle
getLastCudaError
undcheckCudaErrors
der so ziemlich das tut, was in der akzeptierten Antwort beschrieben ist . Demonstrationen finden Sie in den Beispielen. Installieren Sie einfach die Beispiele zusammen mit dem Toolkit und Sie werden es haben.Antworten:
Der wahrscheinlich beste Weg, um nach Fehlern im Laufzeit-API-Code zu suchen, besteht darin, eine Assert-Style-Handler-Funktion und ein Wrapper-Makro wie folgt zu definieren:
Anschließend können Sie jeden API-Aufruf mit dem
gpuErrchk
Makro umschließen, das den Rückgabestatus des umschlossenen API-Aufrufs verarbeitet. Beispiel:Wenn bei einem Aufruf ein Fehler auftritt, wird eine Textnachricht ausgegeben, die den Fehler sowie die Datei und Zeile in Ihrem Code beschreibt, in der der Fehler aufgetreten ist,
stderr
und die Anwendung wird beendet. Sie können möglicherweise Änderungen vornehmengpuAssert
, um eine Ausnahme auszulösen , anstattexit()
eine komplexere Anwendung aufzurufen, wenn dies erforderlich ist.Eine zweite verwandte Frage ist, wie beim Starten von Kerneln nach Fehlern gesucht werden kann, die nicht direkt in einen Makroaufruf wie Standard-Laufzeit-API-Aufrufe eingeschlossen werden können. Für Kernel ungefähr so:
prüft zunächst, ob ein ungültiges Startargument vorliegt, und zwingt den Host dann, zu warten, bis der Kernel stoppt, und nach einem Ausführungsfehler zu suchen. Die Synchronisation kann beseitigt werden, wenn Sie einen nachfolgenden blockierenden API-Aufruf wie folgt haben:
In diesem Fall kann der
cudaMemcpy
Aufruf entweder Fehler zurückgeben, die während der Kernelausführung aufgetreten sind, oder solche aus der Speicherkopie selbst. Dies kann für Anfänger verwirrend sein, und ich würde empfehlen, nach dem Start des Kernels während des Debuggens eine explizite Synchronisierung zu verwenden, um leichter zu verstehen, wo Probleme auftreten können.Beachten Sie, dass bei Verwendung von CUDA Dynamic Parallelism eine sehr ähnliche Methode auf jede Verwendung der CUDA-Laufzeit-API in Gerätekerneln sowie nach dem Start eines Gerätekerns angewendet werden kann und sollte:
quelle
cudaDeviceReset()
bevor wir auch beenden? Und eine Klausel zur Speicherfreigabe?Die obige Antwort von talonmies ist eine gute Möglichkeit, eine Anwendung im
assert
Stil abzubrechen .Gelegentlich möchten wir möglicherweise einen Fehlerzustand in einem C ++ - Kontext als Teil einer größeren Anwendung melden und beheben.
Hier ist eine ziemlich knappe Möglichkeit, dies zu tun, indem Sie eine C ++ - Ausnahme auslösen, die von der
std::runtime_error
Verwendung abgeleitet istthrust::system_error
:Dies beinhaltet den Dateinamen, die Zeilennummer und eine Beschreibung der englischen Sprache
cudaError_t
in das.what()
Mitglied der ausgelösten Ausnahme :Die Ausgabe:
Ein Client von
some_function
kann CUDA-Fehler auf Wunsch von anderen Arten von Fehlern unterscheiden:Weil a
thrust::system_error
iststd::runtime_error
, können wir es alternativ auf die gleiche Weise wie eine breite Klasse von Fehlern behandeln, wenn wir nicht die Genauigkeit des vorherigen Beispiels benötigen:quelle
<thrust/system/cuda_error.h>
ist jetzt effektiv<thrust/system/cuda/error.h>
.Der C ++ - kanonische Weg: Nicht auf Fehler prüfen ... Verwenden Sie die C ++ - Bindungen, die Ausnahmen auslösen.
Früher ärgerte mich dieses Problem. und ich hatte früher eine Lösung mit Makro-Sperma-Wrapper-Funktion, genau wie in den Antworten von Talonmies und Jared, aber ehrlich? Dies macht die Verwendung der CUDA Runtime API noch hässlicher und C-ähnlicher.
Ich habe das also anders und grundlegender angegangen. Ein Beispiel für das Ergebnis finden Sie hier im CUDA-
vectorAdd
Beispiel - mit vollständiger Fehlerprüfung für jeden Laufzeit-API-Aufruf:Erneut - alle potenziellen Fehler werden überprüft, und eine Ausnahme, wenn ein Fehler aufgetreten ist (Einschränkung: Wenn der Kernel nach dem Start einen Fehler verursacht hat , wird er nach dem Versuch, das Ergebnis zu kopieren, nicht vorher abgefangen, um sicherzustellen, dass der Kernel erfolgreich war müssen zwischen dem Start und der Kopie mit einem
cuda::outstanding_error::ensure_none()
Befehl auf Fehler überprüft werden ).Der obige Code verwendet my
Dünne Modern-C ++ - Wrapper für die CUDA Runtime API-Bibliothek (Github)
Beachten Sie, dass die Ausnahmen nach dem fehlgeschlagenen Aufruf sowohl eine Zeichenfolgenerklärung als auch den CUDA-Laufzeit-API-Statuscode enthalten.
Einige Links dazu, wie CUDA-Fehler mit diesen Wrappern automatisch überprüft werden:
quelle
Die hier diskutierte Lösung hat bei mir gut funktioniert. Diese Lösung verwendet integrierte Cuda-Funktionen und ist sehr einfach zu implementieren.
Der entsprechende Code wird unten kopiert:
quelle