Názorný postup: Ladění aplikace AMP C++
Toto téma ukazuje, jak ladit aplikaci, která používá C++ Accelerated Massive Parallelism (C++ AMP) za účelem využití grafického procesoru (GPU).Používá program pro snížení paralelismu, který provede souhrn velkých polí obsahujících celá čísla.Tento návod ilustruje následující úkoly:
Spouštění ladicího nástroje GPU.
Kontrolování vláken GPU v okně GPU vláken.
Použití Okna paralelních zásobníků pro sledování zásobníků volání více vláken GPU najednou.
Použití Okna paralelních zásobníků pro kontrolu jednoho výrazu napříč více vlákny ve stejný okamžik.
Označování, zmrazení, rozmrazování a seskupování GPU vláken.
Spouštění všech vláken dlaždice na určité místo provádění kódu.
Požadavky
Před zahájením tohoto postupu:
Přečtěte si text na stránce Přehled AMP C++.
Ujistěte se, že jsou v textovém editoru zobrazena čísla řádků.Další informace naleznete v tématu Jak: Zobrazovat čísla řádků v editoru.
Přesvědčte se, zda používáte Windows 8 nebo Windows Server 2012 pro podporu ladění v softwarovém emulátoru.
[!POZNÁMKA]
Ve vašem počítači se pro některé z prvků uživatelského rozhraní sady Visual Studio mohou zobrazit jiné názvy a umístění, než jsou uvedeny v následujících pokynech. Tyto prvky jsou určeny verzí aplikace Visual Studio a použitým nastavením. Další informace naleznete v tématu Nastavení aplikace Visual Studio.
Vytvořit ukázkový projekt
Spusťte aplikaci Visual Studio.
V panelu nabídek zvolte Soubor, Nový, Projekt.
Pod záložkou Nainstalováno v podokně šablony vyberte položku Visual C++.
Zvolte Win32 konzolová aplikace, zadejte AMPMapReduce do poli Název a následně klikněte na tlačítko OK.
Klikněte na tlačítko Další.
Nezatrhávejte políčko Předkompilovaná hlavička a následně klikněte na tlačítko Dokončit.
V Průzkumníku řešení odstraňte z projektu stdafx.h, targetver.h a stdafx.cpp.
Otevřete AMPMapReduce.cpp a jeho obsah nahraďte následujícím kódem.
// 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; }
V panelu nabídek vyberte položku Soubor a Uložit vše.
V Průzkumníku řešení otevřete místní nabídku pro AMPMapReduce a pak zvolte Vlastnosti.
V dialogovém okně Stránky vlastností pod Vlastnosti nastavení zvolte C/C++, Předkompilované hlavičky.
Pro vlastnost Předkompilovaná hlavička zvolte Nepoužívat předkompilované hlavičky a následně klikněte na tlačítko OK.
V panelu nabídek zvolte Sestavit, Sestavit řešení.
Ladění kódu procesoru
V tomto postupu použijete místní ladicí program systému Windows pro ověření správnost kódu procesoru v rámci této aplikace.Část kódu procesoru v rámci této aplikace, která je obzvláště zajímavá, je for smyčka ve funkci reduction_sum_gpu_kernel.Ovládá snížení paralelismu založené na stromové struktuře, která je spouštěna na GPU.
Chcete-li ladit kód procesoru
V Průzkumníku řešení otevřete místní nabídku pro AMPMapReduce a pak zvolte Vlastnosti.
V dialogovém okně Stránky vlastností pod Vlastnosti nastavení zvolte Ladění.Ověřte, zda je vybrán Místní ladicí nástroj systému Windows v seznamu Ladící nástroj pro spuštění.
Vraťte se k Editoru kódu.
Body přerušení můžete nastavovat na řádcích kódu v následujícím obrázku (přibližně 67 řádků řádek 70).
Zarážky procesoru
V panelu nabídek zvolte Ladit, Spustit ladění.
V okně Lokální proměnné sledujte hodnotu stride_size, dokud nebude dosažen breakpoint na řádku 70.
V panelu nabídek zvolte položku Ladit, Zastavit ladění.
Ladění kódu GPU
Tato část popisuje ladění kódu GPU, což je kód obsažený v rámci funkce sum_kernel_tiled.Kód GPU paralelně vypočítává celočíselný součet pro každý "blok".
Chcete-li ladit kód GPU
V Průzkumníku řešení otevřete místní nabídku pro AMPMapReduce a pak zvolte Vlastnosti.
V dialogovém okně Stránky vlastností pod Vlastnosti nastavení zvolte Ladění.
V seznamu Ladící nástroj pro spuštění zvolte Místní ladicí nástroj systému Windows.
V seznamu Typ ladícího nástroje zvolte Pouze GPU.
Klikněte na tlačítko OK.
Nastavte bod přerušení na řádku 30, jak je znázorněno na následujícím obrázku.
Body přerušení GPU
V panelu nabídek zvolte Ladit, Spustit ladění.Body přerušení v kódu procesoru na řádcích 67 a 70 řádky nebudou provedeny během ladění GPU, protože jsou tyto řádky kódu prováděny na procesoru.
Použití okna vláken GPU
Chcete-li otevřít okno vláken GPU v panelu nabídek, zvolte položku Ladit, Okna, Vlákna GPU.
Můžete zkontrolovat stav vláken GPU v okně vláken GPU, které se zobrazí.
Ukotvěte okno vláken GPU v dolní části nástroje Visual Studio.Zvolte přepínací tlačítko Rozbalit vlákno pro zobrazení dlaždice a textových polí vlákna.Okno vláken GPU zobrazuje celkový počet aktivních a blokovaných vláken GPU, jak je znázorněno na následujícím obrázku.
Okno vláken GPU
Pro tento výpočet je alokováno 313 dlaždic.Každá dlaždice obsahuje 32 vláken.Protože k místnímu ladění GPU dochází v rámci softwarového emulátoru, existují čtyři aktivní vlákna GPU.Čtyři vlákna provádějí instrukce současně a následně se společně přesouvají na další instrukci.
V okně vláken GPU jsou aktivní čtyři vlákna GPU a 28 vláken GPU blokovaných v příkazu tile_barrier::wait, který je definovaný na řádku 21 (t_idx.barrier.wait();).Všech 32 vláken náleží první dlaždici tile[0].Šipka ukazuje na řádek, který obsahuje aktuální vlákno.Pro přepnutí na jiné vlákno použijte jednu z následujících metod:
V řádku, na který se má přepnout vlákno v rámci okna vláken GPU, otevřete místní nabídku a zvolte Přepnout na vlákno.Pokud řádek představuje více než jedno vlákno, přepnete se na první vlákno podle souřadnic vlákna.
Zadejte hodnoty dlaždice a vlákna do odpovídajících textových polí a následně klikněte na tlačítko Přepnout vlákno.
Okno zásobníku volání zobrazí zásobník volání aktuálního vlákna GPU.
Použití okna paralelních zásobníků
Chcete-li otevřít okno paralelních zásobníků, zvolte v panelu nabídek položku Ladit, Okna, Paralelní zásobníky.
Okno paralelních zásobníků můžete použít pro současnou kontrolu více rámců zásobníku pro více vláken GPU.
Ukotvěte okno paralelních zásobníků v dolní části nástroje Visual Studio.
Ujistěte se, že je v horním rohu seznamu vybraná položka Vlákna.Na následujícím obrázku zobrazuje okno paralelních zásobníků pohled zaměřený na volání zásobníku vláken GPU, které jste mohli vidět v okně GPU vláken.
Okno paralelních zásobníků
32 vláken přešlo z _kernel_stub do výrazu lambda v rámci volání funkce parallel_for_each a následně do funkce sum_kernel_tiled, kde dochází ke snižování paralelismu.28 z 32 vláken postoupilo do příkazu tile_barrier::wait a zůstalo zablokováno na řádku 22, zatímco ostatní 4 vlákna zůstala v rámci funkce sum_kernel_tiled aktivní na řádku 30.
Můžete zkontrolovat vlastnosti vlákna GPU, které jsou k dispozici v okně vláken GPU v rozšíření DataTip okna paralelních zásobníků.Chcete-li provést tuto akci, ponechejte ukazatel myši na rámci zásobníku sum_kernel_tiled.Následující obrázek zobrazuje DataTip.
DataTip vlákna GPU
Další informace o okně paralelních zásobníků získáte na Pomocí okna paralelní hromádky.
Použití okna sledování paralelismu
Chcete-li otevřít okno sledování paralelismu v panelu nabídek, zvolte položku Ladit, Okna, Sledování paralelismu, Sledování paralelismu 1.
Okno sledování paralelismu můžete použít pro kontrolu hodnot výrazu napříč více vlákny.
Ukotvěte okno sledování paralelismu 1 k dolní části nástroje Visual Studio.V tabulce existuje 32 řádků okna sledování paralelismu.Každý z nich odpovídá vláknu GPU, které se objevilo v okně vláken GPU a v okně paralelních zásobníků.Nyní můžete zadat výrazy, jejichž hodnoty chcete kontrolovat napříč všemi 32 vlákny GPU.
Zvolte záhlaví sloupce Přidat sledování, zadejte localIdx a následně stiskněte klávesu Enter.
Znovu zvolte záhlaví sloupce Přidat sledování, zadejte globalIdxa následně stiskněte klávesu Enter.
Znovu zvolte záhlaví sloupce Přidat sledování, zadejte localA[localIdx[0]]a následně stiskněte klávesu Enter.
Zadaný výraz můžete řadit výběrem jeho odpovídajícího záhlaví sloupce.
Vyberte záhlaví sloupce localA [localIdx [0] pro seřazení sloupce.Následující obrázek zobrazuje výsledek řazení podle localA[localIdx [0].
Výsledky řazení
Obsah okna sledování paralelismu můžete exportovat do aplikace Excel výběrem tlačítka Excel a následným zvolením možnosti Otevřít v aplikaci Excel.Pokud máte na počítače, na kterém vyvíjíte nainstalovanou aplikaci Excel, otevře se list aplikace Excel s obsahem.
V pravém horním rohu okna sledování paralelismu se nachází ovládací prvek filtru, který můžete použít k filtrování obsahu použitím logických výrazů.Do textového pole ovládacího prvku filtru zadejte localA[localIdx[0]] > 20000 a následně stiskněte klávesu Enter.
Okno nyní obsahuje pouze vlákna, u nichž je hodnota localA[localIdx[0]] vyšší než 20000.Obsah je nadále seřazen podle sloupce localA[localIdx[0]], který je představuje dříve nastavené řazení.
Označování vláken GPU
Jednotlivá vlákna GPU můžete označit v okně vláken GPU, okně sledování paralelismu, nebo můžete označit DataTip v okně paralelních zásobníků.Pokud řádek v okně vláken GPU obsahuje více, než jedno vlákno, označení tohoto řádku označí všechna vlákna, která jsou obsažená v tomto řádku.
Označení vláken GPU příznakem
Zvolte záhlaví sloupce [podproces] v okně sledování paralelismu 1 pro seřazení podle indexu dlaždice a indexu vlákna.
V panelu nabídek zvolte Ladit, Pokračovat, což způsobí, že čtyři vlákna, která byla aktivní postoupí k další překážce (definováno na řádku 32 AMPMapReduce.cpp).
Zvolte symbol označení příznakem na levé straně řádku, který obsahuje čtyři vlákna, která jsou nyní aktivní.
Následující obrázek zobrazuje čtyři aktivní vlákna označená příznakem v okně vláken GPU.
Aktivní vlákna v okně vláken GPU
Okno sledování paralelismu a DataTip okna paralelních zásobníků ukazují vlákna označená příznakem.
Pokud se chcete zaměřit na čtyři vlákna, která jste označili příznakem, můžete zobrazit v rámci oken vláken GPU, oken sledování paralelismu a oken sledování zásobníků pouze vlákna označená příznakem.
Klikněte na tlačítko Zobrazit pouze s příznakem na libovolné okno nebo na panel nástrojů Umístění ladění.Následující obrázek zobrazuje tlačítko Zobrazit pouze s příznakem na panelu nástrojů Umístění ladění.
Zobrazit tlačítko Pouze s označením příznakem
Okna vláken GPU, sledování paralelismu a paralelních zásobníků zobrazují pouze vlákna označená příznakem.
Zmrazení a uvolňování vláken GPU
Můžete zmrazit (pozastavit) a uvolnit (pokračovat) vlákna GPU buď z okna vláken GPU nebo z okna sledování paralelismu.Můžete zmrazit a uvolnit vlákna GPU stejným způsobem; informace naleznete v tématu Jak: použití okna podprocesů.
Zmrazení a uvolnění vláken GPU
Klikněte na tlačítko Zobrazit pouze označené příznakem pro zobrazení všech vláken.
V panelu nabídek zvolte Ladit, Pokračovat.
Otevřete místní nabídku aktivního řádku a následně zvolte Zmrazit.
Následující obrázek okna vláken GPU zobrazuje zmrazení všech vláken
Zmrazená vlákna v okně vláken GPU
Podobně, okno sledování paralelismu zobrazuje zmražení všech čtyř vláken.
V panelu nabídek zvolte Ladit, Pokračovat pro povolení dalším vláknům GPU v postupu přes překážku na řádku 22 a pro dosažení bodu přerušení na řádku 30.Okno vláken GPU zobrazuje, že čtyři dříve zmrazená vlákna zůstávají zmrazená a v aktivním stavu.
V panelu nabídek zvolte Ladit, Pokračovat.
Z okna sledování paralelismu můžete uvolnit jedno nebo více vláken GPU.
Seskupení vláken GPU
V místní nabídce jednoho z vláken v okně Vlákna GPU zvolte Seskupit, Adresa.
Vlákna v okně vláken GPU jsou seskupena podle adresy.Adresa odpovídá instrukci v rozložení, kde jsou umístěny všechny skupiny vláken.24 vláken se nachází na řádku 22, kde se provádí Metoda tile_barrier::wait.12 vláken se nachází na instrukci překážky na řádku 32.Čtyři z těchto vláken jsou označeny příznakem.Osm vláken se nachází na bodu přerušení na řádku 30.Čtyři z těchto vláken jsou zmrazené.Následující obrázek zobrazuje seskupená vlákna v okně vláken GPU.
Seskupená vlákna v okně vláken GPU
Můžete provést také operaci Seskupit podle otevřením místní nabídky datové mřížky okna sledování paralelismu zvolením položky Seskupit podle a následným výběrem položky nabídky, která odpovídá tomu, jak chcete vlákna seskupit.
Spuštění všech vláken na určitém místě v kódu
Lze spustit všechna vlákna v dané dlaždici do řádku, na který ukazuje kurzor, pomocí Přehrát aktuální dlaždici po kurzor.
Spuštění všech vláken na umístění označené kurzorem
V místní nabídce zmrazených vláken zvolte Uvolnit.
V editoru kódu umístěte kurzor na řádek 30.
V místní nabídce editoru kódu zvolte Přehrát aktuální dlaždici po kurzor.
24 vláken, které byla dříve blokována na překážce na řádku 21, pokročila na řádek 32.Je to znázorněno v okně Vlákna GPU.
Viz také
Úkoly
Jak: použití okna GPU podprocesů
Jak: použití paralelní okno kukátka