Share via


Exemplarische Vorgehensweise: Debuggen einer C++ AMP-Anwendung

Dieses Thema veranschaulicht, wie eine Anwendung debuggen, die C++ beschleunigte enorme Parallelität (C++-AMP) verwendet die Graphikverarbeitungseinheit (GPU) zu nutzen.Es verwendet ein Parallelreduzierung Programm, das ein großes Array ganze Zahlen aufsummiert.In dieser exemplarischen Vorgehensweise werden die folgenden Aufgaben veranschaulicht:

  • Starten der GPU-Debugger.

  • Das Überprüfen von GPU Tabellen im GPU-Threadfenster.

  • Verwenden des parallelen Stapelfensters, die Aufruflisten GPU mehrere Tabellen gleichzeitig zu beobachten.

  • Verwenden des parallelen Überwachungsfensters, um Werte eines einzelnen Ausdrucks über mehrere Threads gleichzeitig zu überprüfen.

  • GPU-Threads kennzeichnen, Fixieren, Entsperren und Gruppierung.

  • Ausführen aller Threads einer Kachel an einer bestimmten Position im Code.

Vorbereitungsmaßnahmen

Bevor Sie mit dieser exemplarischen Vorgehensweise beginnen:

HinweisHinweis

Ihr Computer zeigt möglicherweise für einige der Elemente der Visual Studio-Benutzeroberfläche in der folgenden Anleitung andere Namen oder Standorte an. Diese Elemente sind von der jeweiligen Visual Studio-Version und den verwendeten Einstellungen abhängig. Weitere Informationen finden Sie unter Visual Studio-Einstellungen.

So erstellen Sie das Beispielprojekt

  1. Starten Sie Visual Studio.

  2. Klicken Sie auf der Menüleiste wählen Sie Datei, Neu, Projekt aus.

  3. Die Installiert im Vorlagenbereich, wählen Sie Visual C++ aus.

  4. Wählen Sie Win32-Konsolenanwendung aus, geben Sie AMPMapReduceName im Feld ein, und wählen Sie dann die Schaltfläche OK aus.

  5. Wählen Sie die Schaltfläche Weiter aus.

  6. Deaktivieren Sie das Kontrollkästchen Vorkompilierter Header, und wählen Sie dann die Schaltfläche Fertig stellen aus.

  7. In Projektmappen-Explorer, in der Löschvorgang stdafx.h, targetver.h und stdafx.cpp aus dem Projekt.

  8. Öffnen Sie AMPMapReduce.cpp und ersetzen Sie den Inhalt mit dem folgenden Code.

    // AMPMapReduce.cpp defines the entry point for the program.
    // The program performs a parallel-sum reduction that computes the sum of an array of integers. 
    
    #include <stdio.h>
    #include <tchar.h>
    #include <amp.h>
    
    const int BLOCK_DIM = 32;
    
    using namespace concurrency;
    
    void sum_kernel_tiled(tiled_index<BLOCK_DIM> t_idx, array<int, 1> &A, int stride_size) restrict(amp)
    {
        tile_static int localA[BLOCK_DIM];
    
        index<1> globalIdx = t_idx.global * stride_size;
        index<1> localIdx = t_idx.local;
    
        localA[localIdx[0]] =  A[globalIdx];
    
        t_idx.barrier.wait();
    
        // Aggregate all elements in one tile into the first element.
        for (int i = BLOCK_DIM / 2; i > 0; i /= 2) 
        {
            if (localIdx[0] < i) 
            {
    
                localA[localIdx[0]] += localA[localIdx[0] + i];
            }
    
            t_idx.barrier.wait();
        }
    
        if (localIdx[0] == 0)
        {
            A[globalIdx] = localA[0];
        }
    }
    
    int size_after_padding(int n)
    {
        // The extent might have to be slightly bigger than num_stride to 
        // be evenly divisible by BLOCK_DIM. You can do this by padding with zeros.
        // The calculation to do this is BLOCK_DIM * ceil(n / BLOCK_DIM)
        return ((n - 1) / BLOCK_DIM + 1) * BLOCK_DIM;
    }
    
    int reduction_sum_gpu_kernel(array<int, 1> input) 
    {
        int len = input.extent[0];
    
        //Tree-based reduction control that uses the CPU.
        for (int stride_size = 1; stride_size < len; stride_size *= BLOCK_DIM) 
        {
            // Number of useful values in the array, given the current
            // stride size.
            int num_strides = len / stride_size;  
    
            extent<1> e(size_after_padding(num_strides));
    
            // The sum kernel that uses the GPU.
            parallel_for_each(extent<1>(e).tile<BLOCK_DIM>(), [&input, stride_size] (tiled_index<BLOCK_DIM> idx) restrict(amp)
            {
                sum_kernel_tiled(idx, input, stride_size);
            });
        }
    
        array_view<int, 1> output = input.section(extent<1>(1));
        return output[0];
    }
    
    int cpu_sum(const std::vector<int> &arr) {
        int sum = 0;
        for (size_t i = 0; i < arr.size(); i++) {
            sum += arr[i];
        }
        return sum;
    }
    
    std::vector<int> rand_vector(unsigned int size) {
        srand(2011);
    
        std::vector<int> vec(size);
        for (size_t i = 0; i < size; i++) {
            vec[i] = rand();
        }
        return vec;
    }
    
    array<int, 1> vector_to_array(const std::vector<int> &vec) {
        array<int, 1> arr(vec.size());
        copy(vec.begin(), vec.end(), arr);
        return arr;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        std::vector<int> vec = rand_vector(10000);
        array<int, 1> arr = vector_to_array(vec);
    
        int expected = cpu_sum(vec);
        int actual = reduction_sum_gpu_kernel(arr);
    
        bool passed = (expected == actual);
        if (!passed) {
            printf("Actual (GPU): %d, Expected (CPU): %d", actual, expected);
        }
        printf("sum: %s\n", passed ? "Passed!" : "Failed!"); 
    
        getchar();
    
        return 0;
    }
    
  9. Klicken Sie auf der Menüleiste wählen Sie Datei, Alle speichern aus.

  10. In Projektmappen-Explorer öffnen Sie das Kontextmenü für AMPMapReduce, und wählen Sie dann Eigenschaften aus.

  11. Eigenschaftenseiten im Dialogfeld unter Konfigurationseigenschaften, wählen Sie C/C++, Vorkompilierte Header aus.

  12. Für die Eigenschaft Vorkompilierter Header wählen ausgewähltes Vorkompilierte Header nicht verwenden und dann die Schaltfläche OK aus.

  13. Klicken Sie auf der Menüleiste wählen Sie Erstellen, Projektmappe erstellen aus.

Debuggen des Codes CPU

In diesem Verfahren verwenden Sie den lokalen Windows-Debugger, um sicherzustellen, dass der CPUcode in dieser Anwendung richtig ist.Das Segment des CPUcodes in dieser Anwendung, die besonders interessant ist, ist die for Schleife in der reduction_sum_gpu_kernel-Funktion.Es steuert die strukturbasierte parallele reduziert, die auf das GPU ausgeführt wird.

Um den CPUcode debuggen

  1. In Projektmappen-Explorer öffnen Sie das Kontextmenü für AMPMapReduce, und wählen Sie dann Eigenschaften aus.

  2. Eigenschaftenseiten im Dialogfeld unter Konfigurationseigenschaften, wählen Sie Debugging aus.Stellen Sie sicher, dass Lokaler Windows-DebuggerZu startender Debugger in der Liste ausgewählt ist.

  3. Kehren Sie zum Code-Editor zurück.

  4. Legen Sie Haltepunkte in den Codezeilen, die in der folgenden Abbildung dargestellt wurden (zeichnet ungefähr 67 Zeile 70).

    CPUhaltepunkte

    CPU-Haltepunkte

  5. Klicken Sie in der Menüleiste auf Debuggen und dann auf Debuggen starten.

  6. Im Fenster Lokal beobachten Sie den Wert für stride_size, bis der Haltepunkt in Zeile 70 erreicht wird.

  7. Klicken Sie auf der Menüleiste wählen Sie Debuggen, Debuggen beenden aus.

Debuggen des GPU-Codes

In diesem Abschnitt wird erläutert, wie der GPU-Code, den der Code ist, enthält in der sum_kernel_tiled-Funktion debuggen.Der GPU-Code berechnet die Summe von ganzen Zahlen für jeden "Block" parallel.

Um den GPU-Code debuggen

  1. In Projektmappen-Explorer öffnen Sie das Kontextmenü für AMPMapReduce, und wählen Sie dann Eigenschaften aus.

  2. Eigenschaftenseiten im Dialogfeld unter Konfigurationseigenschaften, wählen Sie Debugging aus.

  3. In der Liste Zu startender Debugger ausgewähltes Lokaler Windows-Debugger.

  4. In der Liste Debuggertyp ausgewähltes Nur GPU.

  5. Klicken Sie auf die Schaltfläche OK.

  6. Legen Sie einen Haltepunkt in Zeile 30, wie in der folgenden Abbildung dargestellt.

    GPU-Haltepunkt

    GPU-Haltepunkte

  7. Klicken Sie in der Menüleiste auf Debuggen und dann auf Debuggen starten.Die Haltepunkte im CPUcode an Zeilen 67 und 70 werden nicht während GPU-Debuggings ausgeführt, da diese Codezeilen für die CPU ausgeführt werden.

Um das GPU-Threadfenster verwenden

  1. Um das GPU-Threadfenster, auf der Menüleiste zu öffnen, wählen Sie Debuggen, Windows, GPU-Threads.

    Sie können den Zustand überprüfen, den das GPU im GPU-Threadfenster vorliegt, das angezeigt wird.

  2. Docken Sie das GPU-Threadfenster am unteren Rand von Visual Studio an.Wählen Sie die Schaltfläche, um die Erweitern Sie Thread-Schalter Kachel- und Threadtextfelder anzuzeigen.Das GPU Tabellen wird die Gesamtzahl aktiver und blockierten GPU-Threads, wie in der folgenden Abbildung dargestellt.

    GPU Tabellen Fenster

    GPU-Threadfenster mit vier aktiven Threads

    Es gibt 313 Kacheln, die für diese Berechnung zugeordnet werden.Jede Kachel enthält 32 Threads.Da lokales GPU-Debugging auf einem Software-Emulator auftritt, gibt es vier aktive GPU-Threads.Die vier Threads führen die Anweisungen gleichzeitig aus und wechseln dann zusammen mit der nächsten Anweisung weiter.

    Im GPU-Threadfenster gibt es vier GPU-Threads, die aktiv sind und 28 GPU-Threads, die an der tile_barrier::wait-Anweisung blockiert werden, die ungefähr von Zeile 21 (t_idx.barrier.wait();) definiert ist.Alle 32 GPU-Threads gehören der ersten Kachel, tile[0].Ein Pfeil zeigt auf die Zeile, die den aktuellen Thread enthält.Um zu einem anderen Thread zu wechseln, verwenden Sie eine der folgenden Methoden:

    • In der Zeile, sodass der Thread wechselt zu GPU-Threads in das Fenster, öffnen Sie das Kontextmenü und wählen Sie Zu Thread wechseln aus.Wenn die Zeile mehr als einen Thread darstellt, wechseln Sie zum ersten Thread entsprechend den Threadkoordinaten um.

    • Geben Sie die Kachel- und Threadwerte des Threads in die entsprechenden Textfelder ein und klicken Sie dann auf die Schaltfläche Threadwechsel aus.

    Das Fenster Aufrufliste wird die Aufrufliste des Threads des aktuellen GPU an.

Um das Fenster Parallele Stapel verwenden

  1. Um das Fenster Parallele Stapel, auf der Menüleiste zu öffnen, wählen Sie Debuggen, Windows, Parallele Stapel.

    Sie können das parallele Stapelfenster verwenden, um die Stapelrahmen mehrerer GPU-Threads gleichzeitig zu überprüfen.

  2. Docken Sie das parallele Stapelfenster am unteren Rand von Visual Studio an.

  3. Stellen Sie sicher, dass Threads in der Liste in der oberen linken Ecke ausgewählt ist.In der folgenden Abbildung die Vogelperspektive angezeigt Aufrufliste eine fokussierte Ansicht der GPU-Threads, die Sie im GPU Thread- Fenster kennen gelernt haben.

    Fenster Parallele Stapel

    Fenster für parallele Stapel mit vier aktiven Threads

    32 Threads gingen von _kernel_stub zur Lambda-Anweisung im parallel_for_each-Funktionsaufruf und dann zur sum_kernel_tiled-Funktion, in der die parallele Verringerung auftritt.28 aus den 32 Threads heraus sind bis zu der tile_barrier::wait-Anweisung vorangeschritten und bleiben in Zeile 22 blockiert, während andere 4 Threads in der sum_kernel_tiled-Funktion in Zeile 30 aktiv bleiben.

    Sie können die Eigenschaften eines GPU-Threads überprüfen, die im GPU-Threadfenster in umfangreichen DataTip des parallelen Stapelfensters verfügbar sind.Hierzu, Positionieren Sie den Mauszeiger auf dem Stapelrahmen von sum_kernel_tiled still.Die folgende Abbildung zeigt DataTip an.

    GPU-Thread DataTip

    DataTip für Fenster für parallele Stapel

    Weitere Informationen über die parallele Stapel Fenster, finden Sie unter Verwenden des Fensters "Parallele Stapel".

Um das parallele Überwachungsfenster verwenden

  1. Um das parallele Überwachungsfenster, auf der Menüleiste zu öffnen, wählen Sie Debuggen, Windows, Parallele Überwachung, Parallele Überwachung 1.

    Sie können das parallele Überwachungsfenster verwenden, um die Werte eines Ausdrucks über mehrere Threads untersuchen.

  2. Docken Sie das parallele Fenster die Überwachung 1 am unteren Rand von Visual Studio an.Es gibt 32 Zeilen in der Tabelle des parallelen Überwachungsfensters.Jedes entspricht einem GPU-Thread, der im GPU-Threadfenster und im Fenster Parallele Stapel befindet.Jetzt können Sie Ausdrücke eingeben, deren Werte Sie über alle 32 GPU-Threads überprüfen möchten.

  3. Wählen Sie den Überwachung hinzufügen Spaltenheader aus, geben Sie localIdx ein und wählen Sie dann die EINGABETASTE aus.

  4. Wählen Sie den Überwachung hinzufügen Spaltenheader erneut aus, geben Sie globalIdx ein und wählen Sie dann die EINGABETASTE aus.

  5. Wählen Sie den Überwachung hinzufügen Spaltenheader erneut aus, geben Sie localA [localIdx [0]] ein und wählen Sie dann die EINGABETASTE aus.

    Sie können über einen angegebenen Ausdruck sortieren, indem Sie den zugehörigen Spaltenheader auswählen.

    Wählen Sie den localA[localIdx[0]] Spaltenheader aus, um die Spalte zu sortieren.Die folgende Abbildung zeigt die Ergebnisse der Sortierung nach localA[localIdx[0]] an.

    Ergebnisse der Sortierung

    Fenster für parallele Stapel mit sortierten Ergebnissen

    Sie können den Inhalt im parallelen Überwachungsfenster in Excel exportieren, indem Sie die " auswählen und dann In Excel öffnen auswählen.Wenn Sie Excel verfügen, die auf dem Entwicklungscomputer installiert sind, wird dieser ein Excel-Arbeitsblatt, das den Inhalt enthält.

  6. In der rechten oberen Ecke des parallelen Überwachungsfensters gibt es ein Filtersteuerelement, das Sie verwenden können, um den Inhalt zu filtern, indem Sie boolesche Ausdrücke verwenden.Geben Sie localA [localIdx [0]] > 20000 im Filtertextgrundlagefeld ein und wählen Sie dann die EINGABETASTE aus.

    Das Fenster enthält jetzt nur Threads, in denen der localA[localIdx[0]]-Wert größer als 20000 ist.Der Inhalt wird weiterhin über die localA[localIdx[0]] Spalte sortiert, die die Sortierungsaktion ist, die Sie zuvor ausgeführt haben.

Kennzeichnen von GPU-Threads

Sie können bestimmte GPU-Threads markieren, indem Sie sie im GPU-Threadfenster, im parallelen Überwachungsfenster oder in einem DataTip im parallelen Stapelfenster kennzeichnen.Wenn eine Zeile im GPU-Threadfenster mehr als einem Thread enthält und kennzeichnet, dass Zeile alle Threads kennzeichnet, die der Zeile enthalten sind.

So GPU-Threads kennzeichnen

  1. Wählen Sie den [Thread] Spaltenheader im parallelen Fenster die Überwachung 1 das Sortieren nach Kachelindex aus und den gewünschten Thread Index.

  2. Klicken Sie auf der Menüleiste wählen Sie Debuggen, Weiter aus, die die vier Threads verursacht, die aktiv waren, bis zu der folgenden Barriere Teamstatus (definiert in Zeile 32 der AMPMapReduce.cpp).

  3. Wählen Sie das Kennzeichnungssymbol auf der linken Seite der Zeile aus, die die vier Threads enthält, die jetzt aktiv sind.

    Die folgende Abbildung zeigt die vier aktiven gekennzeichneten Threads im GPU-Threadfenster an.

    Aktive Threads im Fenster GPU Tabellen

    GPU-Threadfenster mit gekennzeichneten Threads

    Das parallele Überwachungsfenster und DataTip des parallelen Stapelfensters beide geben die gekennzeichneten Threads an.

  4. Wenn Sie sich auf die vier Threads konzentrieren möchten, die Sie bezeichnet, Sie wählen können, um, in den GPU-Threads, in der Ähnlichkeits-Überwachung und in den Ähnlichkeits-Stapelfenstern zu veranschaulichen, nur die gekennzeichneten Threads.

    Wählen Sie den weiteren gekennzeichnete Schaltfläche nur auf einem der Fenster oder auf der Symbolleiste Debugspeicherort aus.Die folgende Abbildung zeigt der werden nur gekennzeichnete Schaltfläche auf der Symbolleiste Debugspeicherort an.

    zeigen nur gekennzeichnete Schaltfläche

    Symbolleiste „Debugspeicherort“ mit Symbol für „Nur gekennzeichnete anzeigen“

    Jetzt zeigen die GPU-Threads, die parallele Überwachungs- und parallelen die Stapelfenster nur die gekennzeichneten Threads an.

GPU-Threads Sperren und Entsperren von

Sie können (Zusammenfassung) GPU-Threads entweder vom GPU-Threadfenster oder vom parallelen Überwachungsfenster fixieren (ab) und reaktivieren.Sie können CPUthreads einfrieren und aktivieren die gleiche Methode; Informationen zu finden Sie unter Gewusst wie: Verwenden des Threadfensters.

So GPU-Threads einfrieren und aktivieren

  1. Wählen Sie die Schaltfläche Nur gekennzeichnete anzeigen, um alle Threads anzuzeigen.

  2. Klicken Sie auf der Menüleiste wählen Sie Debuggen, Weiter aus.

  3. Öffnen Sie das Kontextmenü für die aktuelle Zeile und wählen Sie dann Fixieren aus.

    Die folgende Abbildung des GPU-Threadfensters zeigt, dass alle vier Threads fixiert werden.

    Fixierte Threads im Fenster GPU Tabellen

    GPU-Threadfenster mit fixierten Threads

    Ebenso stellt das parallele Überwachungsfenster dar, dass alle vier Threads fixiert werden.

  4. Wählen Sie auf der Menüleiste Sie Debuggen, Weiter, um die folgenden vier GPU-Threads zum Status hinter der Barriere in Zeile 22 zu ermöglichen und den Haltepunkt in Zeile 30 zu erreichen.Das GPU-Threadfenster zeigt, dass die vier zuvor eingefrorenen Threads und im aktiven Zustand fixiert bleiben.

  5. Klicken Sie auf der Menüleiste wählen Sie Debuggen, Weiter aus.

  6. Wählen Sie parallelen Überwachungsfenster können Sie einzelne oder mehrere GPU-Threads auch erneut aktivieren.

So GPU-Threads gruppieren

  1. Klicken Sie im Kontextmenü für einen der Threads im Fenster GPU-Threads, wählen Sie Gruppieren nach, Adresse aus.

    Die Threads im GPU-Threadfenster werden durch Adresse gruppiert.Die Adresse entspricht der Anweisung in der Disassembly, in der jede Gruppe Threads befindet.24 Threads sind in Zeile 22, wobei tile_barrier::wait-Methode ausgeführt wird.12 Threads sind an der Anweisung für die Barriere in Zeile 32.Vier dieser Threads werden gekennzeichnet.Es gibt acht Threads sind am Haltepunkt in Zeile 30.Vier dieser Threads werden fixiert.Die folgende Abbildung zeigt die gruppierten Threads im GPU-Threadfenster an.

    Gruppierte Tabellen GPU Threads im Fenster

    GPU-Threadfenster mit nach Adresse angeordneten Threads

  2. Sie können den Gruppieren nach Vorgang ausführen, indem Sie das Kontextmenü für das Datenraster des parallelen Überwachungsfensters öffnen, Gruppieren nach auswählen, und anschließend das Menüelement auswählen, das entspricht, wie Sie die Threads gruppieren möchten.

Ausführen aller Threads auf eine bestimmte Position im Code

Sie führen alle Threads in einer angegebenen Kachel zur Zeile aus, die den Cursor enthält, indem Sie Aktuelle Kachel bis zum Cursor ausführen verwendet.

Um alle Threads zum Speicherort ausführen gekennzeichnet durch den Cursor

  1. Klicken Sie im Kontextmenü für die fixierten Threads, wählen Sie Reaktivieren aus.

  2. Im Code-Editor platzieren Sie den Cursor in Zeile 30 ein.

  3. Klicken Sie im Kontextmenü für den Code-Editor, wählen Sie Aktuelle Kachel bis zum Cursor ausführen aus.

    Die 24 Threads, die zuvor an der Barriere in Zeile 21 blockiert wurden, sind vorangeschritten, um 32 zu zeichnen.Dies wird im Fenster GPU-Threads angezeigt.

Siehe auch

Aufgaben

Gewusst wie: Verwenden des Fensters "GPU-Threads"

Gewusst wie: Verwenden des parallelen Überwachungsfensters

Konzepte

Übersicht über C++ AMP

Weitere Ressourcen

Debuggen von GPU-Code

Analysieren von C++-AMP Code mit der Parallelitätsschnellansicht