Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In dit artikel wordt gedemonstreerd hoe u fouten kunt opsporen in een toepassing die gebruikmaakt van C++ Accelerated Massive Parallelism (C++ AMP) om te profiteren van de GPU (Graphics Processing Unit). Het maakt gebruik van een programma voor parallelle reductie waarmee een grote matrix met gehele getallen wordt opgeteld. Deze handleiding illustreert de volgende taken:
- Het GPU-foutopsporingsprogramma starten.
- Gpu-threads inspecteren in het venster GPU-threads.
- Gebruik het venster Parallelle stacks om tegelijkertijd de aanroepstacks van meerdere GPU-threads te observeren.
- Gebruik het venster Parallel Watch om waarden van één expressie in meerdere threads tegelijk te controleren.
- Gpu-threads markeren, blokkeren, ontdooien en groeperen.
- Alle threads van een tegel uitvoeren op een specifieke locatie in code.
Vereiste voorwaarden
Voordat je aan deze handleiding begint:
Opmerking
C++ AMP-headers worden afgeschaft vanaf Visual Studio 2022 versie 17.0.
Als u AMP-headers opneemt, zullen er buildfouten optreden. Definieer _SILENCE_AMP_DEPRECATION_WARNINGS voordat u AMP-headers opneemt om de waarschuwingen te onderdrukken.
- Lees C++ AMP-overzicht.
- Zorg ervoor dat regelnummers worden weergegeven in de teksteditor. Zie Procedure: Regelnummers weergeven in de editor voor meer informatie.
- Zorg ervoor dat u ten minste Windows 8 of Windows Server 2012 uitvoert ter ondersteuning van foutopsporing op de softwareemulator.
Opmerking
Mogelijk worden op uw computer verschillende namen of locaties weergegeven voor sommige elementen van de Visual Studio-gebruikersinterface in de volgende instructies. De Visual Studio-editie die u hebt en de instellingen die u gebruikt, bepalen deze elementen. Zie Personalizing the IDEvoor meer informatie.
Het voorbeeldproject maken
De instructies voor het maken van een project variëren, afhankelijk van de versie van Visual Studio die u gebruikt. Zorg ervoor dat u de juiste documentatieversie hebt geselecteerd boven de inhoudsopgave op deze pagina.
Het voorbeeldproject maken in Visual Studio
Kies bestand>nieuw>project op de menubalk om het dialoogvenster Nieuw project maken te openen.
Stel bovenaan het dialoogvenster Taal in op C++, stel Platform in op Windows en stel Projecttype in op Console.
Kies Console-app in de gefilterde lijst met projecttypen en kies vervolgens Volgende. Voer
AMPMapReduceop de volgende pagina het vak Naam in om een naam voor het project op te geven en geef de projectlocatie op als u een andere locatie wilt.
Kies de knop Maken om het clientproject te maken.
Het voorbeeldproject maken in Visual Studio 2017 of Visual Studio 2015
Start Visual Studio.
Kies Bestand>nieuw>project op de menubalk.
Kies Visual C++ onder Geïnstalleerd in het deelvenster Sjablonen.
Kies Win32-consoletoepassing, typ
AMPMapReducein het vak Naam en kies vervolgens de knop OK .Kies de knop Volgende .
Schakel het selectievakje Vooraf gecompileerde koptekst uit en kies vervolgens de knop Voltooien .
Verwijder in Solution Explorerstdafx.h, targetver.h en stdafx.cpp uit het project.
Volgend:
Open AMPMapReduce.cpp en vervang de inhoud door de volgende 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; }Kies Op de menubalk de optie Alles>opslaan.
Open in Solution Explorer het snelmenu voor AMPMapReduce en kies vervolgens Eigenschappen.
Kies in het dialoogvenster Eigenschappenpagina's onder Configuratie-eigenschappende optie C/C++>Vooraf gecompileerde headers.
Voor de eigenschap Vooraf gecompileerde koptekst selecteert u Niet gebruiken van vooraf gecompileerde headers en kiest u vervolgens de knop OK .
Kies Build>Oplossing bouwen in de menubalk.
Fouten opsporen in de CPU-code
In deze procedure gebruikt u het lokale Windows-foutopsporingsprogramma om ervoor te zorgen dat de CPU-code in deze toepassing juist is. Het segment van de CPU-code in deze toepassing dat vooral interessant is, is de for lus in de reduction_sum_gpu_kernel functie. Het controleert de boom-gebaseerde parallelle reductie die wordt uitgevoerd op de GPU.
Fouten opsporen in de CPU-code
Open in Solution Explorer het snelmenu voor AMPMapReduce en kies vervolgens Eigenschappen.
Kies In het dialoogvenster Eigenschappenpagina's onder Configuratie-eigenschappen de optie Foutopsporing. Controleer of Local Windows Debugger is geselecteerd in de lijst Debugger om te starten.
Ga terug naar de Code-editor.
Stel onderbrekingspunten in op de coderegels die worden weergegeven in de volgende afbeelding (ongeveer regel 67 regel 70).
CPU-onderbrekingspuntenKies op de menubalk de foutopsporing>starten.
Bekijk in het venster Locals de waarde voor
stride_sizetotdat het onderbrekingspunt op regel 70 is bereikt.Kies in de menubalk de optie Foutopsporing> stoppen.
Fouten opsporen in de GPU-code
In deze sectie wordt beschreven hoe u fouten in de GPU-code kunt opsporen. Dit is de code in de sum_kernel_tiled functie. De GPU-code berekent de som van gehele getallen voor elk 'blok' parallel.
Fouten opsporen in de GPU-code
Open in Solution Explorer het snelmenu voor AMPMapReduce en kies vervolgens Eigenschappen.
Kies In het dialoogvenster Eigenschappenpagina's onder Configuratie-eigenschappen de optie Foutopsporing.
Selecteer Local Windows Debugger in de lijst met foutopsporingsprogramma's om te starten.
Controleer in de lijst Foutopsporingsprogrammatype of Automatisch is geselecteerd.
Auto is de standaardwaarde. In versies vóór Windows 10 is GPU alleen de vereiste waarde in plaats van Automatisch.
Kies de knop OK.
Stel een onderbrekingspunt in op regel 30, zoals wordt weergegeven in de volgende afbeelding.
GPU-onderbrekingspuntKies op de menubalk de foutopsporing>starten. De onderbrekingspunten in de CPU-code op regel 67 en 70 worden niet uitgevoerd tijdens GPU-foutopsporing omdat deze regels code worden uitgevoerd op de CPU.
Het venster GPU-threads gebruiken
Als u het venster GPU-threads wilt openen, kiest u op de menubalk de optie Fouten opsporen> inWindows>GPU-threads.
U kunt de status controleren van de GPU-threads in het venster GPU-threads dat wordt weergegeven.
Dock het GPU Threads-venster onderaan Visual Studio. Kies de knop Threadschakelaar uitvouwen om de tekstvakken van de tegel en de thread weer te geven. In het venster GPU-threads ziet u het totale aantal actieve en geblokkeerde GPU-threads, zoals wordt weergegeven in de volgende afbeelding.
Het venster GPU-threads313 tegels worden toegewezen voor deze berekening. Elke tegel bevat 32 threads. Omdat lokale GPU-foutopsporing optreedt in een softwareemulator, zijn er vier actieve GPU-threads. De vier threads voeren de instructies tegelijkertijd uit en gaan vervolgens verder met de volgende instructie.
In het venster GPU-threads zijn er vier GPU-threads actief en 28 GPU-threads geblokkeerd bij de tile_barrier::wait-instructie die ongeveer op regel 21 is gedefinieerd (
t_idx.barrier.wait();). Alle 32 GPU-threads behoren tot de eerste tegel,tile[0]. Een pijl wijst naar de rij die de huidige thread bevat. Gebruik een van de volgende methoden om over te schakelen naar een andere thread:In de rij van de thread waarnaar moet worden overgeschakeld in het venster GPU-threads, open het snelmenu en kies Overschakelen naar thread. Als de rij meer dan één thread vertegenwoordigt, schakelt u over naar de eerste thread op basis van de threadcoördinaten.
Voer de tegel- en threadwaarden van de thread in de bijbehorende tekstvakken in en kies vervolgens de knop Thread wisselen .
In het venster Aanroepstack wordt de aanroepstack van de huidige GPU-thread weergegeven.
Het venster Parallelle stacks gebruiken
Als u het venster Parallelle stacks wilt openen, kiest u op de menubalk de optie Fouten opsporen> inWindows>Parallel Stacks.
U kunt het venster Parallel Stacks gebruiken om tegelijkertijd de stackframes van meerdere GPU-threads te inspecteren.
Dock het venster Parallel Stacks aan de onderkant van Visual Studio.
Zorg ervoor dat Threads is geselecteerd in de lijst in de linkerbovenhoek. In de volgende afbeelding ziet u in het venster Parallelle stacks een aanroepstack-gerichte weergave van de GPU-threads die u in het venster GPU-threads hebt gezien.
Venster Parallelle Stapels32 threads gingen van
_kernel_stubnaar de lambda-instructie in deparallel_for_eachfunctie-aanroep en vervolgens naar desum_kernel_tiledfunctie, waar de parallelle reductie plaatsvindt. 28 van de 32 threads zijn naar detile_barrier::waitinstructie gegaan en blijven geblokkeerd op regel 22, terwijl de andere vier threads actief blijven in desum_kernel_tiledfunctie op regel 30.U kunt de eigenschappen van een GPU-thread inspecteren. Ze zijn beschikbaar in het venster GPU-threads in de uitgebreide DataTip van het venster Parallelle stacks . Als u ze wilt zien, beweegt u de aanwijzer op het stapelframe van
sum_kernel_tiled. In de volgende afbeelding ziet u de DataTip.
GPU thread DataTipZie voor meer informatie over het venster Parallelle stapels, Het venster Parallelle stapels gebruiken.
Het venster Parallel Watch gebruiken
Als u het venster Parallel Watch wilt openen, kiest u in de menubalk de optie Fouten opsporen> inWindows>Parallel Watch>Parallel Watch 1.
U kunt het venster Parallel Watch gebruiken om de waarden van een expressie in meerdere threads te controleren.
Dock het venster Parallel Watch 1 aan de onderkant van Visual Studio. Er staan 32 rijen in de tabel van het venster Parallel Watch . Elk komt overeen met een GPU-thread die wordt weergegeven in zowel het venster GPU-threads als het venster Parallelle stacks . U kunt nu expressies invoeren waarvan u de waarden wilt controleren op alle 32 GPU-threads.
Selecteer de kolomkop Watch toevoegen, voer
localIdxin en kies vervolgens de Enter-toets.Selecteer opnieuw de kolomkop Controle toevoegen, typ
globalIdx, en druk daarna op de Enter-toets.Selecteer opnieuw de kolomkop Controle toevoegen, typ
localA[localIdx[0]], en druk daarna op de Enter-toets.U kunt sorteren op een opgegeven expressie door de bijbehorende kolomkop te selecteren.
Selecteer de kolomkop localA[localIdx[0]] om de kolom te sorteren. In de volgende afbeelding ziet u de resultaten van sorteren op localA[localIdx[0]].
Resultaten van sorterenU kunt de inhoud in het venster Parallel Watch exporteren naar Excel door de knop Excel te kiezen en vervolgens Openen in Excel te kiezen. Als u Excel op uw ontwikkelcomputer hebt geïnstalleerd, wordt met de knop een Excel-werkblad geopend dat de inhoud bevat.
In de rechterbovenhoek van het venster Parallel Watch bevindt zich een filterbesturingselement dat u kunt gebruiken om de inhoud te filteren met behulp van Boole-expressies. Voer
localA[localIdx[0]] > 20000in het tekstvak van het filterbesturingselement in en druk vervolgens op de Enter-toets.Het venster bevat nu alleen threads waarop de
localA[localIdx[0]]waarde groter is dan 20000. De inhoud wordt nog steeds gesorteerd op delocalA[localIdx[0]]kolom. Dit is de sorteeractie die u eerder hebt gekozen.
GPU-threads markeren
U kunt specifieke GPU-threads markeren door ze te markeren in het venster GPU-threads , het venster Parallel Watch of de DataTip in het venster Parallelle stacks . Als een rij in het GPU-Threads venster meer dan één thread bevat, worden alle threads in die rij gemarkeerd als u die rij markeert.
GPU-threads markeren
Selecteer de kolomkop [Thread] in het venster Parallel Watch 1 om te sorteren op tegelindex en threadindex.
Kies op de menubalk Debug en daarna >, waardoor de vier actieve threads naar de volgende barrière worden voortbewogen (gedefinieerd op regel 32 van AMPMapReduce.cpp).
Kies het vlagsymbool aan de linkerkant van de rij met de vier threads die nu actief zijn.
In de volgende afbeelding ziet u de vier actieve threads met vlag in het venster GPU-threads .
Actieve threads in het venster GPU-threadsHet venster Parallel Watch en de DataTip van het venster Parallel Stacks geven beide de gemarkeerde threads aan.
Als u zich wilt richten op de vier threads die u hebt gemarkeerd, kunt u ervoor kiezen om alleen de gemarkeerde threads weer te geven. Het beperkt wat u ziet in de GPU Threads, Parallel Watch en Parallel Stacks-vensters .
Kies de knop Alleen met vlag weergeven in een van de vensters of op de werkbalk Locatie voor foutopsporing . In de volgende afbeelding ziet u de knop Alleen met vlag weergeven op de werkbalk Locatie voor foutopsporing .
Alleen gemarkeerd weergeven knopNu geven de GPU-threads, parallelle controle en parallelle stacks alleen de gemarkeerde threads weer.
Gpu-threads blokkeren en ontdooien
U kunt GPU-threads blokkeren (onderbreken) en ontdooien (hervatten) vanuit het venster GPU-threads of het venster Parallel Watch . U kunt CPU-threads op dezelfde manier blokkeren en ontdooien; Zie Instructies voor meer informatie: Het venster Threads gebruiken.
GPU-threads blokkeren en ontdooien
Kies de knop Alleen met vlag weergeven om alle threads weer te geven.
Kies Op de menubalk de optie Doorgaan voor foutopsporing>.
Open het snelmenu voor de actieve rij en kies Vervolgens Blokkeren.
In de volgende afbeelding van het venster GPU-threads ziet u dat alle vier threads zijn geblokkeerd.
Geblokkeerde threads in het venster GPU-threadsOp dezelfde manier ziet u in het venster Parallel Watch dat alle vier de threads zijn geblokkeerd.
Kies op de menubalk de optie Foutopsporing>Doorgaan om toe te staan dat de volgende vier GPU-threads voorbij de barrière op regel 22 gaan en het onderbrekingspunt op regel 30 bereiken. In het GPU-venster ziet u dat de vier eerder bevroren threads bevroren blijven en actief zijn.
Kies Fouten opsporen in de menubalk, Doorgaan.
Vanuit het venster Parallel Watch kunt u ook afzonderlijke of meerdere GPU-threads ontdooien.
Om GPU-threads te groeperen
Kies in het snelmenu voor een van de threads in het venster GPU-threads, Groeperen op basis vanAdres.
De threads in het venster GPU-threads worden gegroepeerd op adres. Het adres komt overeen met de instructie in de disassemblage waar elke groep van threads zich bevindt. 24 threads bevinden zich op regel 22 waar de methode tile_barrier::wait wordt uitgevoerd. 12 draadjes staan bij de instructie voor de barrière op regel 32. Vier van deze threads worden gemarkeerd. Acht threads bevinden zich op het onderbrekingspunt op regel 30. Vier van deze threads zijn bevroren. In de volgende afbeelding ziet u de gegroepeerde threads in het venster GPU-threads .
Gegroepeerde threads in het venster GPU-threadsU kunt de bewerking Groeperen ook uitvoeren door het snelmenu voor het gegevensraster van het venster Parallel Watch te openen. Selecteer Groeperen op en kies vervolgens het menu-item dat overeenkomt met de wijze waarop u de threads wilt groeperen.
Alle threads uitvoeren op een specifieke locatie in code
U voert alle threads in een bepaalde tegel uit naar de regel waar de cursor zich bevindt met behulp van Huidige tegel naar cursor uitvoeren.
Alle threads uitvoeren op de locatie die is gemarkeerd door de cursor
Kies Thaw in het snelmenu voor de bevroren threads.
Plaats de cursor op regel 30 in de Code Editor.
Kies in het snelmenu voor de Code-editor de optie Voer huidige tegel uit tot cursor.
De 24 threads die eerder bij de barrière op regel 21 werden geblokkeerd, zijn naar regel 32 verplaatst. Deze wordt weergegeven in het venster GPU-threads .
Zie ook
Overzicht van C++ AMP
Foutopsporing van GPU-code
Procedure: Het venster GPU-threads gebruiken
Procedure: Het venster Parallel Watch gebruiken
Analyseren van C++ AMP-code met de Concurrency Visualizer