4 Stimmen

Zeit zwischen dem Start des Kernels und der Ausführung des Kernels

Ich versuche, mein CUDA-Programm zu optimieren, indem ich die Parallel Nsight 2.1 Edition für VS 2010 verwende.

Mein Programm läuft auf einer Windows 7 (32-Bit)-Maschine mit einer GTX 480-Karte. Ich habe das CUDA 4.1 32-Bit-Toolkit und den 301.32-Treiber installiert.

Ein Zyklus im Programm besteht aus einer Kopie von Host-Daten auf das Gerät, der Ausführung der Kernele und der Kopie der Ergebnisse vom Gerät auf den Host.

Wie Sie im Bild der Profilerergebnisse unten sehen können, werden die Kernele in vier verschiedenen Streams ausgeführt. Der Kernel in jedem Stream basiert auf den auf das Gerät kopierten Daten in 'Stream 2'. Deshalb wird das AsyncMemcpy mit der CPU synchronisiert, bevor die Kernele in den verschiedenen Streams gestartet werden.

Bildbeschreibung hier eingeben

Was mich in dem Bild irritiert, ist die große Lücke zwischen dem Ende des ersten Kernelstarts (bei 10.5778679285) und dem Beginn der Kernelausführung (bei 10.5781500). Es dauert etwa 300 us, um den Kernel zu starten, was ein enormer Overhead in einem Verarbeitungszyklus von weniger als 1 ms ist.

Darüber hinaus gibt es keine Überlappung der Kernelausführung und der Datenkopie der Ergebnisse zurück zum Host, was den Overhead noch weiter erhöht.

Gibt es offensichtliche Gründe für dieses Verhalten?

6voto

Greg Smith Punkte 10527

Es gibt drei Probleme, die ich anhand des Traces erkennen kann.

  1. Nsight CUDA Analysis fügt etwa 1 µs pro API-Aufruf hinzu. Sie haben sowohl den CUDA-Runtime- als auch den CUDA-Treiber-API-Trace aktiviert. Wenn Sie den CUDA-Runtime-Trace deaktivieren würden, würde ich vermuten, dass Sie die Dauer um 50 µs reduzieren könnten.

  2. Da Sie auf einem GTX 480 unter Windows 7 arbeiten, verwenden Sie das WDDM-Treibermodell. Bei WDDM muss der Treiber einen Kernel-Aufruf tätigen, um die Arbeit zu übergeben, was eine Menge Overhead verursacht. Um diesen Overhead zu reduzieren, puffert der CUDA-Treiber Anfragen in einer internen SW-Warteschlange und sendet die Anfragen an den Treiber, wenn die Warteschlange voll ist, wird sie durch einen Synchronisierungsaufruf geleert. Es ist möglich, cudaEventQuery zu verwenden, um den Treiber zur Arbeitserledigung zu zwingen, aber dies kann andere Leistungsauswirkungen haben.

  3. Es scheint, als würden Sie Ihre Arbeit in Streams auf eine tiefer gestaffelte Weise übermitteln. Auf Geräten mit den Compute-Fähigkeiten 2.x und 3.0 erzielen Sie bessere Ergebnisse, wenn Sie die Arbeit in Streams auf eine breiter gestaffelte Weise übermitteln. In Ihrem Fall könnten Sie eine Überlappung zwischen Ihren Kernels sehen.

Der Zeitschienen-Screenshot liefert nicht ausreichende Informationen, um festzustellen, warum die Speicherübertragungen nach Abschluss aller Kernels beginnen. Anhand des API-Aufrufsmusters sollten Sie sehen können, dass die Übertragungen beginnen, nachdem jeder Stream seinen Start abgeschlossen hat.

Wenn Sie darauf warten, dass alle Streams abgeschlossen sind, ist es wahrscheinlich schneller, einen cudaDeviceSynchronize durchzuführen als 4 cudaStreamSynchronize-Aufrufe.

Die nächste Version von Nsight wird zusätzliche Funktionen haben, um das Verständnis der SW-Warteschlange und der Übermittlung von Arbeit an den Berechnungsmotor und den Speicherübertragungsmotor zu erleichtern.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X