Wskazówki: debugowanie aplikacji C++ AMP
W tym temacie przedstawiono sposób debugowania aplikacji korzystającej z C++ Accelerated Massive Parallelism (C++ AMP) umożliwiającej wykorzystanie procesora karty graficznej (GPU).Używa programu redukcji równoległej, który sumuje duże tablice liczb całkowitych.W przewodniku przedstawiono następujące zagadnienia:
Uruchamianie debugera GPU.
Podgląd wątków GPU w oknie Wątków GPU.
Korzystanie z okna stosów równoległych do jednoczesnego podglądu stosów wywołań wielu wątków GPU.
Za pomocą okna Czujki równoległej można podejrzeć wartości pojedynczego wyrażenia przez wiele wątków jednocześnie.
Flagowanie, zamrażanie, rozmrażanie i grupowanie wątków GPU.
Wykonywanie wszystkich wątków fragmentu do określonej lokalizacji w kodzie.
Wymagania wstępne
Przed rozpoczęciem przewodnika:
Przeczytaj Przegląd C++ AMP.
Upewnij się, że w edytorze tekstu wyświetlane są numery wierszy.Aby uzyskać więcej informacji, zobacz Porady: Wyświetlanie numerów wierszy w Edytorze.
Upewnij się, czy jest uruchomiony Windows 8 lub Windows Server 2012 do obsługi debugowania emulatora oprogramowania.
[!UWAGA]
Na komputerze w poniższych instrukcjach mogą być wyświetlane inne nazwy i lokalizacje niektórych elementów interfejsu użytkownika programu Visual Studio. Te elementy są określane przez numer wersji Visual Studio oraz twoje ustawienia. Aby uzyskać więcej informacji, zobacz Dostosowywanie ustawień środowiska deweloperskiego w Visual Studio.
Aby utworzyć przykładowy projekt
Uruchom program Visual Studio.
W pasku menu wybierz Plik, Nowy, Projekt.
W oknie Zainstalowane w okienku szablony wybierz Visual C++.
Wybierz typ Aplikacja konsolowa Win32, AMPMapReduce w polu Nazwa, a następnie wybierz przycisk OK.
Wybierz przycisk Dalej.
Wyczyść pole wyboru Nagłówek prekompilowany, a następnie wybierz przycisk Zakończ.
Usuń z projektu stdafx.h, targetver.h i stdafx.cpp w Eksploratorze rozwiązania.
Otwórz AMPMapReduce.cpp i zastąp zawartość następującym kodem.
// 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; }
Na pasku menu wybierz Plik, Zapisz wszystkie.
W Eksploratorze rozwiązania, otwórz menu kontekstowe dla AMPMapReduce, a następnie wybierz Właściwości.
W oknie dialogowym Strony właściwości, pod Właściwości konfiguracji, wybierz C/C++, Prekompilowane nagłówki.
Dla właściwości Prekompilowane nagłówki, zaznacz Nie są używane prekompilowane nagłówki, a następnie wybierz przycisk OK.
Na pasku menu wybierz Kompilacja, Skompiluj rozwiązanie.
Debugowanie kodu procesora
W tej procedurze zostanie użyty lokalny debuger systemu Windows, aby upewnić się, że kod procesora w aplikacji jest poprawny.Szczególnie interesujący segment kodu procesora w tej aplikacji znajduje się w pętli for w funkcji reduction_sum_gpu_kernel.Kontroluje redukcję równoległą opartą o drzewa, uruchamianą na procesorze GPU.
Aby debugować kod procesora
W Eksploratorze rozwiązania, otwórz menu kontekstowe dla AMPMapReduce, a następnie wybierz Właściwości.
W oknie dialogowym Strony właściwości, pod Właściwości konfiguracji, wybierz Debugowanie.Sprawdź, czy opcja Lokalny debuger systemu Windows jest zaznaczona w liście Uruchamiany debuger.
Powróć do edytora kodu.
Punkty przerwania można ustawić na liniach kodu pokazano na poniższej ilustracji (linie ok. 67 i 70).
Punkty przerwań procesora
Na pasku menu wybierz Debugowanie, Rozpocznij debugowanie.
W oknie Zmienne lokalne należy obserwować wartości dla stride_size aż do osiągnięcia punktu przerwania w wierszu 70.
Na pasku menu wybierz Debugowanie, Zatrzymaj debugowanie.
Debugowanie kodu GPU
W tej sekcji przedstawiono sposób debugowania kodu procesora GPU, który jest zawarty w funkcji sum_kernel_tiled.Kod procesora GPU oblicza sumy całkowite dla każdego "bloku" równoległego.
Aby debugować kod procesora GPU
W Eksploratorze rozwiązania, otwórz menu kontekstowe dla AMPMapReduce, a następnie wybierz Właściwości.
W oknie dialogowym Strony właściwości, pod Właściwości konfiguracji, wybierz Debugowanie.
Z listy Debuger do uruchomienia wybierz Lokalny debuger systemu Windows.
W liście Typów debugera wybierz Tylko GPU.
Wybierz przycisk OK.
Ustaw punkt przerwania w wierszu 30, tak jak pokazano na poniższej ilustracji.
Punkt przerwania procesora GPU
Na pasku menu wybierz Debugowanie, Rozpocznij debugowanie.Punkty przerwania w kodzie procesora w wierszach 67 i 70 nie są wykonywane podczas debugowania GPU, ponieważ te wiersze kodu są wykonywane na procesorze.
Aby korzystać z okna Wątków GPU
Aby otworzyć okno wątków GPU, wybierz Debugowanie na pasku menu , Okna, Wątki GPU.
Można sprawdzić stan wątków GPU w wyświetlonym oknie Wątków GPU.
Zadokuj okno wątków GPU na dole okna programu Visual Studio.Wybierz Rozwiń przełączenie wątku przycisk, aby wyświetlić pola tekstowe fragmentu i wątku.Okno Wątków GPU pokazuje całkowitą liczbę aktywnych i zablokowanych wątków GPU, tak jak pokazano na poniższej ilustracji.
Okno wątków GPU
Istnieje 313 fragmentów przydzielonych dla tego wyliczenia.Każdy fragment zawiera 32 wątki.Ponieważ lokalne debugowanie GPU zachodzi na programowym emulatorze, dostępne są cztery wątki aktywne GPU.Cztery wątki równocześnie wykonują instrukcję, a następnie przechodzą razem do następnej instrukcji.
W oknie GPU wątków są cztery wątki GPU active i 28 GPU wątki zablokowane na tile_barrier::wait instrukcji zdefiniowanych na temat linia 21 (t_idx.barrier.wait();).Wszystkie 32 wątki GPU należą do pierwszego fragmentu tile[0].Strzałka wskazuje na wiersz, który zawiera bieżący wątek.Aby przełączyć się do innego wątku, należy użyć jednej z następujących metod:
W wierszu dla wątku do którego chcesz się przełączyć w oknie wątków GPU, otwórz menu kontekstowe i wybierz polecenie Przełącz się do wątku.Jeśli wiersz reprezentuje więcej niż jeden wątek, przełączenie zostanie wykonane na pierwszy wątek zgodnie ze współrzędnymi wątku.
Wprowadź wartości fragmentu i wątku w odpowiadających polach tekstowych, a następnie kliknij przycisk Przełącz wątek.
W oknie Stos wywołań wyświetlany jest stos wywołań bieżącego wątku GPU.
Aby skorzystać z okna Stosy równoległe
Aby otworzyć okno Stosy równoległe, na pasku menu wybierz polecenia Debuguj, Okna, Stosy równoległe.
Można użyć okna Stosy równoległe, aby jednocześnie podejrzeć ramki stosów wielu wątków GPU.
Zadokuj okno Stosy równoległe u dołu okna programu Visual Studio.
Upewnij się, że Wątki są zaznaczone na liście w lewym górnym rogu.Na poniższej ilustracji okno stosów równoległych pokazuje widok wątków procesora GPU skoncentrowany na stosie wywołań widzianym w oknie wątków procesora GPU.
Okno Stosów równoległych
32 wątki zmieniły się od _kernel_stub do wyrażenia lambda w wywołaniu funkcji parallel_for_each i następnie do funkcji sum_kernel_tiled, gdzie występuje redukcja równoległa. 28 z 32 wątków poczyniło postęp do instrukcji tile_barrier::wait i pozostają zablokowane w wierszu 22, natomiast 4 wątki pozostają aktywne w funkcji sum_kernel_tiled w wierszu 30.
Można sprawdzić właściwości wątku GPU, które są dostępne w oknie wątków procesora GPU w sformatowanym elemencie DataTip okna stosów równoległych.Aby to zrobić, ustaw wskaźnik myszy na ramce stosu sum_kernel_tiled.Na następującej ilustracji pokazano DataTip.
Wątek GPU DataTip
Aby uzyskać więcej informacji o oknie stosów równoległych, zobacz Korzystanie z okna stosów równoległych.
Aby korzystać z okna Czujka równoległa
Aby otworzyć okno Czujka równoległa, na pasku menu wybierz Debugowanie, Okna, Czujka równoległa, Czujka równoległa 1.
Można użyć okna Czujka równoległa do sprawdzenia wartości wyrażenia na przestrzeni wielu wątków.
Zadokuj okno Czujka równoległa 1 u dołu programu Visual Studio.W tabeli w oknie czujki równoległej znajdują się 32 wiersze.Każdy odpowiada wątkowi GPU, który pojawił się zarówno w oknach wątków GPU i stosów równoległych.Teraz można wprowadzać wyrażenia, których wartości mają zostać sprawdzone w obrębie wszystkich 32 wątków GPU.
Wybierz nagłówek kolumny Dodaj czujkę, wprowadź localIdx, a następnie wybierz klawisz Enter.
Wybierz ponownie nagłówek kolumny Dodaj czujkę, typ globalIdx, a następnie wybierz klawisz Enter.
Wybierz nagłówek kolumny Dodaj czujkę ponownie, typ localA[localIdx[0], a następnie wybierz klawisz Enter.
Można sortować według określonego wyrażenia, zaznaczając nagłówek odpowiadającej mu kolumny.
Wybierz nagłówek kolumny localA[localIdx[0], aby posortować kolumnę.Na poniższej ilustracji przedstawiono wyniki sortowania według localA[localIdx[0].
Wyniki sortowania
Zawartość w oknie czujki równoległych można wyeksportować do programu Excel, wybierając przycisk programu Excel, a następnie wybierając Otwórz w programie Excel.Jeśli program Excel jest zainstalowany na komputerze dewelopera zostanie otwarty arkusz programu Excel zawierający zawartość.
W prawym górnym rogu okna Czujka równoległa znajduje się kontrolka filtru, którego można użyć do filtrowania zawartości przy użyciu wyrażeń logicznych.ENTER localA [localIdx [0] >20000 w tekst formantu filtru pole, a następnie wybierz klawisz Enter.
Okno to zawiera teraz tylko wątki, w których wartość localA[localIdx[0]] jest większa niż 20000.Zawartość jest wciąż posortowana według kolumny localA[localIdx[0]], która jest wynikiem sortowania wykonywanym wcześniej.
Flagowanie wątków GPU
Można oznaczyć określone wątki GPU przez flagowanie ich w oknie wątków GPU, okno czujki równoległej lub DataTip w oknie stosów równoległych .Jeśli wiersz w oknie wątków GPU zawiera więcej niż jeden wątek, oflagowanie tego wiersza flaguje wszystkie wątki, które są zawarte we wierszu.
Aby oflagować wątki GPU
Wybierz nagłówek kolumny [Wątek] w oknie Czujka równoległa 1 aby sortować według indeksu fragmentu i indeksu wątku.
Na pasku menu wybierz Debugowanie, Kontynuuj, co spowoduje, że cztery wątki, które były aktywne, przejdą do następnej bariery (zdefiniowanej w wierszu 32. AMPMapReduce.cpp).
Wybierz symbol flagi po lewej stronie wiersza, który zawiera cztery wątki, które są teraz aktywne.
Na następującej ilustracji przedstawiono cztery aktywne wątki oflagowane w oknie Wątki GPU.
Aktywne wątki w oknie wątków GPU
Zarówno Okno czujki równoległej jak i DataTip okna stosów równoległych wskazują oflagowane wątki.
Aby skoncentrować się na czterech wątkach, które oflagowano, można wybrać pokazywanie tylko wątków oflagowanych w oknach Wątki GPU, Czujka równoległa, Stosy równoległe.
Pokaż przycisk Tylko oflagowane na jednym z okien lub na pasku narzędzi Lokalizacja debugowania.Na poniższej ilustracji przedstawiono przycisk Pokaż tylko oflagowane dla paska narzędzi Lokalizacja debugowania.
Przycisk Pokaż tylko oflagowane
Teraz okna Wątków GPU, Czujki równoległej i Stosów równoległych wyświetlają tylko oflagowane wątki.
Zamrażanie i odmrażanie wątków GPU
Można zamrozić (zawiesić) i odmrozić (wznowić) wątki GPU z okna Wątki GPU lub okna Czujki równoległej.Użytkownik może zamrażać i rozmrażać wątki procesora w ten sam sposób; Aby uzyskać informacje, zobacz Porady: korzystanie z okna wątków.
Aby zamrozić lub rozmrozić wątki GPU
Wybierz przycisk Pokaż tylko oflagowane aby wyświetlić wszystkie wątki.
Na pasku menu wybierz Debugowanie, Kontynuuj.
Otwórz menu kontekstowe dla aktywnego wiersza, a następnie wybierz polecenie Zamroź.
Poniższa ilustracja okna GPU wątków pokazuje, że wszystkie cztery wątki są zamrożone.
Zamrożone wątki w oknie wątków GPU
Podobnie okno Czujki równoległej pokazuje, że wszystkie cztery wątki są zamrożone.
Na pasku menu wybierz Debugowanie, Kontynuuj aby umożliwić czterem wątkom GPU przekroczenie bariery w wierszu 22 i dotarcie do punktu przerwania w wierszu 30.Okno wątków GPU pokazuje, że cztery wątki zamrożone wcześniej pozostają zamrożone i w stanie aktywnym.
Na pasku menu wybierz Debugowanie, Kontynuuj.
Z okna czujki równoległej można również rozmrażać pojedyncze wątki lub wiele wątków GPU.
Aby pogrupować wątki GPU
W menu kontekstowym dla jednego z wątków w oknie Wątki GPU, wybierz Grupuj według, Adres.
Wątki w oknie Wątki GPU są pogrupowane według adresu.Adres odnosi się do instrukcji w dezasemblacji, w którym znajduje się każda grupa wątków. 24 wątki znajdują się w linii 22 gdzie wykonywana jest metoda tile_barrier::wait — Metoda. W instrukcji dla bariery w linii 32 jest 12 wątków.Cztery wątki spośród nich są oznaczone flagą.Osiem wątków znajduje się w punkcie przerwania w wierszu 30.Cztery wątki spośród nich są zamrożone.Następujące ilustracji pokazano wątki zgrupowane w oknie Wątków GPU.
Wątki zgrupowane w oknie Wątków GPU
Można również wykonać operację Grupuj według poprzez otwarcie menu kontekstowego dla siatki danych z okna Czujka równoległa, wybranie Grupuj według i wybierając element menu, który odpowiada sposobu grupowania wątków.
Uruchamianie wszystkich wątków do określonej lokalizacji w kodzie
Można uruchomić wszystkie wątki w danym fragmencie do wiersza zawierającego kursor za pomocą Uruchom bieżący fragment do kursora.
Aby uruchomić wszystkie wątki do miejsca oznaczonego kursorem
W menu kontekstowym dla zamrożonych wątków wybierz polecenie Rozmroź.
W edytorze kodu, umieść kursor w wierszu 30.
W menu kontekstowym dla edytora kodu wybierz Uruchom bieżący fragment do kursora.
24 wątki, które były wcześniej zablokowane na barierze w wierszu 21, wykonały się do wiersza 32.Jest to pokazane w oknie Wątki GPU.
Zobacz też
Zadania
Porady: korzystanie z okna wątków GPU
Porady: korzystanie z okna czujki równoległej