Probleme beim Start von CUDA-Kernel aus dem statischen Initialisierungscode

8

Ich habe eine Klasse, die einen Kernel in ihrem Konstruktor wie folgt aufruft:

"ScalarField.h"

%Vor%

"classA.h"

%Vor%

"main.cu"

%Vor%

Wenn ich diese Klasse auf main ( A a_object; ) instanziiere, erhalte ich keine Fehler. Wenn ich es jedoch außerhalb von main instanziiere, gleich nachdem ich es definiert habe ( class A {...} a_object; ), erhalte ich beim Start des Kernels den Fehler "ungültige Gerätefunktion". Warum passiert das?

BEARBEITEN

Aktualisierter Code, um ein vollständigeres Beispiel zu bieten.

BEARBEITEN 2

Dem Ratschlag in dem Kommentar von Raxvan folgend, wollte ich sagen, dass ich die Variable dimensions , die im ScalarField-Konstruktor verwendet wird, auch außerhalb von main, aber vor allem anderen (in einer anderen Klasse) definiert habe. Könnte das die Erklärung sein? Der Debugger zeigte jedoch den richtigen Wert für dimensions .

    
Noel 21.07.2014, 15:43
quelle

1 Antwort

12

Die kurze Version:

Der Grund für das Problem, wenn class A außerhalb von main instanziiert wird, ist, dass eine bestimmte Hook-Routine, die zum Initialisieren der CUDA-Laufzeitbibliothek mit Ihren Kerneln benötigt wird, nicht ausgeführt wird, bevor der Konstruktor von class A aufgerufen wird . Dies geschieht, weil es keine Garantien für die Reihenfolge gibt, in der statische Objekte im C ++ - Ausführungsmodell instanziiert und initialisiert werden. Ihre globale Bereichsklasse wird instanziiert, bevor die globalen Bereichsobjekte initialisiert werden, die das CUDA-Setup initialisieren. Ihr Kernel-Code wird niemals vor dem Aufruf in den Kontext geladen, und es tritt ein Laufzeitfehler auf.

Soweit ich das beurteilen kann, ist dies eine echte Einschränkung der CUDA-Laufzeit-API und nicht etwas, das im Benutzercode leicht zu beheben ist. In Ihrem trivialen Beispiel könnten Sie den Kernel-Aufruf durch einen Aufruf von cudaMemset oder eine der nicht symbolbasierten Laufzeit-API-memset-Funktionen ersetzen und es wird funktionieren. Dieses Problem ist vollständig auf Benutzerkerne oder Gerätesymbole beschränkt, die zur Laufzeit über die Laufzeit-API geladen werden. Aus diesem Grund würde ein leerer Standardkonstruktor auch Ihr Problem lösen. Aus der Sicht des Designs wäre ich sehr fragwürdig, ob ein Muster Kernel im Konstruktor aufruft. Das Hinzufügen einer spezifischen Methode für das GPU-Setup / Teardown der Klasse, die nicht auf dem Standardkonstruktor oder Destruktor beruht, wäre ein viel saubereres und weniger fehleranfälliges Design, IMHO.

Im Detail:

Es gibt eine intern generierte Routine ( __cudaRegisterFatBinary ), die ausgeführt werden muss, um Kernel, Texturen und statisch definierte Gerätesymbole in der Fatbin-Payload eines beliebigen Runtime-API-Programms mit der CUDA-Treiber-API zu laden und zu registrieren, bevor der Kernel sein kann ohne Fehler aufgerufen. Dies ist ein Teil der "faulen" Kontextinitialisierungsfunktion der Laufzeit-API. Sie können dies wie folgt selbst bestätigen:

Hier ist ein gdb-Trace des überarbeiteten Beispiels, das Sie gepostet haben. Hinweis: Ich füge einen Haltepunkt in __cudaRegisterFatBinary ein, der nicht erreicht wird, bevor der statische A -Konstruktor aufgerufen wird und der Kernel-Start fehlschlägt:

%Vor%

Hier ist die gleiche Prozedur, diesmal mit A instanziation innerhalb von main (was garantiert geschieht, nachdem die Objekte, die das träge Setup ausführen, initialisiert wurden):

%Vor%

Wenn das wirklich ein lähmendes Problem für Sie ist, würde ich vorschlagen, den NVIDIA-Entwickler-Support zu kontaktieren und einen Fehlerbericht zu erstellen.

    
talonmies 22.07.2014, 09:29
quelle