Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este artigo demonstra como depurar um aplicativo que usa C++ Accelerated Massive Parallelism (C++ AMP) para tirar proveito da unidade de processamento gráfico (GPU). Ele usa um programa de redução paralela que resume uma grande variedade de inteiros. Este passo a passo ilustra as seguintes tarefas:
- Iniciando o depurador GPU.
- A inspeção das threads da GPU na janela de Threads da GPU.
- Usando a janela Pilhas paralelas para observar simultaneamente as pilhas de chamadas de vários threads de GPU.
- Usando a janela Inspeção paralela para inspecionar valores de uma única expressão em vários threads ao mesmo tempo.
- Marcação, congelamento, descongelamento e agrupamento de GPU threads.
- Executando todas as threads de um tile para um local específico no código.
Pré-requisitos
Antes de começar este passo a passo:
Observação
Os cabeçalhos AMP C++ foram preteridos a partir do Visual Studio 2022 versão 17.0.
A inclusão de cabeçalhos AMP gerará erros de compilação. Defina _SILENCE_AMP_DEPRECATION_WARNINGS antes de incluir quaisquer cabeçalhos AMP para silenciar os avisos.
- Leia Visão geral do C++ AMP.
- Certifique-se de que os números de linha são exibidos no editor de texto. Para obter mais informações, consulte Como exibir números de linha no editor.
- Certifique-se de que está a executar pelo menos o Windows 8 ou o Windows Server 2012 para suportar a depuração no emulador de software.
Observação
Seu computador pode mostrar nomes ou locais diferentes para alguns dos elementos da interface do usuário do Visual Studio nas instruções a seguir. A edição do Visual Studio que você tem e as configurações que você usa determinam esses elementos. Para obter mais informações, consulte Personalizando o IDE.
Para criar o projeto de exemplo
As instruções para criar um projeto variam dependendo de qual versão do Visual Studio você está usando. Certifique-se de que tem a versão correta da documentação selecionada acima do índice nesta página.
Para criar o projeto de exemplo no Visual Studio
Na barra de menus, escolha Arquivo>Novo>Projeto para abrir a caixa de diálogo Criar um Novo Projeto .
Na parte superior da caixa de diálogo, defina Language como C++, defina Platform como Windows e defina Project type como Console.
Na lista filtrada de tipos de projeto, escolha Aplicativo de console e, em seguida, escolha Avançar. Na página seguinte, digite
AMPMapReducena caixa Nome para especificar um nome para o projeto e especifique o local do projeto, se desejar um diferente.
Escolha o botão Criar para criar o projeto cliente.
Para criar o projeto de exemplo no Visual Studio 2017 ou Visual Studio 2015
Inicie o Visual Studio.
Na barra de menus, escolha Arquivo>Novo>Projeto.
Em Instalado no painel de modelos, escolha Visual C++.
Escolha Aplicativo de console Win32, digite
AMPMapReducena caixa Nome e, em seguida, escolha o botão OK .Escolha o botão Next .
Desmarque a caixa de seleção Cabeçalho pré-compilado e escolha o botão Concluir .
No Gerenciador de Soluções, exclua stdafx.h, targetver.h e stdafx.cpp do projeto.
A seguir:
Abra AMPMapReduce.cpp e substitua seu conteúdo pelo código a seguir.
// 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 barra de menus, escolha Arquivo>Salvar tudo.
No Gerenciador de Soluções, abra o menu de atalho para AMPMapReduce e escolha Propriedades.
Na caixa de diálogo Páginas de Propriedades, em Propriedades de Configuração, escolha Cabeçalhos Pré-compilados>.
Para a propriedade Cabeçalho pré-compilado , selecione Não usar cabeçalhos pré-compilados e escolha o botão OK .
Na barra de menus, escolha Build>Build Solution.
Depurando o código da CPU
Neste procedimento, você usará o Depurador Local do Windows para certificar-se de que o código da CPU neste aplicativo está correto. O segmento do código da CPU nesta aplicação que é especialmente interessante é o for loop na reduction_sum_gpu_kernel função. Ele controla a redução paralela baseada em árvore que é executada na GPU.
Para depurar o código da CPU
No Gerenciador de Soluções, abra o menu de atalho para AMPMapReduce e escolha Propriedades.
Na caixa de diálogo Páginas de Propriedades, em Propriedades de Configuração, escolha Depuração. Verifique se o Depurador Local do Windows está selecionado na lista Depurador para iniciar .
Volte ao Editor de Códigos.
Defina pontos de interrupção nas linhas de código mostradas na ilustração a seguir (aproximadamente linhas 67, linha 70).
Pontos de interrupção da CPUNa barra de menus, escolha Depurar>Iniciar Depuração.
Na janela Locais, observe o valor de
stride_sizeaté que o ponto de interrupção na linha 70 seja atingido.Na barra de menus, escolha Depurar>Parar Depuração.
Depurando o código da GPU
Esta seção mostra como depurar o código da GPU, que é o código contido na sum_kernel_tiled função. O código da GPU calcula a soma de inteiros para cada "bloco" em paralelo.
Para depurar o código da GPU
No Gerenciador de Soluções, abra o menu de atalho para AMPMapReduce e escolha Propriedades.
Na caixa de diálogo Páginas de Propriedades, em Propriedades de Configuração, escolha Depuração.
Na lista Depurador a ser iniciado , selecione Depurador Local do Windows.
Na lista Tipo de Depurador , verifique se Auto está selecionado.
Auto é o valor padrão. Em versões anteriores ao Windows 10, Somente GPU é o valor necessário em vez de Auto.
Escolha o botão OK.
Defina um ponto de interrupção na linha 30, conforme mostrado na ilustração a seguir.
Ponto de interrupção da GPUNa barra de menus, escolha Depurar>Iniciar Depuração. Os pontos de interrupção no código da CPU nas linhas 67 e 70 não são executados durante a depuração da GPU porque essas linhas de código são executadas na CPU.
Para usar a janela GPU Threads
Para abrir a janela GPU Threads , na barra de menus, escolha Debug>Windows>GPU Threads.
Você pode inspecionar o estado dos threads da GPU na janela GPU Threads que aparece.
Encaixe a janela GPU Threads na parte inferior do Visual Studio. Escolha o botão Expandir Interruptor de thread para exibir as caixas de texto de bloco e thread. A janela GPU Threads mostra o número total de threads de GPU ativos e bloqueados, conforme mostrado na ilustração a seguir.
Janela de Threads de GPU313 blocos são alocados para este cálculo. Cada tile contém 32 threads. Como a depuração de GPU local ocorre em um emulador de software, há quatro threads de GPU ativos. Os quatro threads executam as instruções simultaneamente e, em seguida, passam juntos para a próxima instrução.
Na janela Threads GPU, há quatro threads GPU ativos e 28 threads GPU bloqueados na instrução tile_barrier::wait definida aproximadamente na linha 21 (
t_idx.barrier.wait();). Todos os 32 threads da GPU pertencem ao primeiro bloco,tile[0]. Uma seta aponta para a linha que inclui o thread atual. Para alternar para um thread diferente, use um dos seguintes métodos:Na linha para a qual o thread deve alternar na janela GPU Threads , abra o menu de atalho e escolha Switch To Thread. Se a linha representar mais de um thread, você alternará para o primeiro thread de acordo com as coordenadas do thread.
Insira os valores de quadrícula e linha nas caixas de texto correspondentes e escolha o botão Alternar linha.
A janela Pilha de chamadas exibe a pilha de chamadas do thread da GPU atual.
Para utilizar a janela Pilhas Paralelas
Para abrir a janela Pilhas Paralelas, na barra de menus, escolha Depurar>Windows>Pilhas Paralelas.
Você pode usar a janela Pilhas paralelas para inspecionar simultaneamente os quadros de pilha de vários threads de GPU.
Encaixe a janela Pilhas paralelas na parte inferior do Visual Studio.
Certifique-se de que Threads está selecionado na lista no canto superior esquerdo. Na ilustração a seguir, a janela Pilhas Paralelas mostra uma visão focada na pilha de chamadas dos threads da GPU que se visualiza na janela Threads da GPU.
Janela de Pilhas Paralelas32 threads foram de
_kernel_stubpara a instrução lambda na chamada de funçãoparallel_for_eache, em seguida, para a funçãosum_kernel_tiled, onde ocorre a redução paralela. 28 dos 32 threads avançaram para a instruçãotile_barrier::waite permanecem bloqueados na linha 22, enquanto os outros quatro threads continuam ativos na funçãosum_kernel_tiledna linha 30.Você pode inspecionar as propriedades de um thread de GPU. Estão disponíveis na janela GPU Threads no abrangente DataTip da janela Parallel Stacks. Para vê-los, passe o ponteiro sobre o quadro de pilha de
sum_kernel_tiled. A ilustração a seguir mostra a DataTip.
Dica de dados de thread da GPUPara obter mais informações sobre a janela Pilhas paralelas , consulte Usando a janela Pilhas paralelas.
Para utilizar a janela de Inspeção Paralela
Para abrir a janela Observação Paralela, na barra de menus, escolha Depurar>Windows>Observação Paralela>Observação Paralela 1.
Você pode usar a janela Inspeção paralela para inspecionar os valores de uma expressão em vários threads.
Encaixe a janela Parallel Watch 1 na parte inferior do Visual Studio. Há 32 linhas na tabela da janela Parallel Watch . Cada um corresponde a um thread de GPU que apareceu na janela GPU Threads e na janela Parallel Stacks . Agora, você pode inserir expressões cujos valores deseja inspecionar em todos os 32 threads da GPU.
Selecione o cabeçalho da coluna Add Watch , digite
localIdxe, em seguida, escolha a tecla Enter .Selecione o cabeçalho da coluna Adicionar observação novamente, digite
globalIdx, e depois pressione a tecla Enter.Selecione o cabeçalho da coluna Adicionar observação novamente, digite
localA[localIdx[0]], e depois pressione a tecla Enter.Você pode classificar por uma expressão especificada selecionando o cabeçalho da coluna correspondente.
Selecione o cabeçalho da coluna localA[localIdx[0]] para classificar a coluna. A ilustração a seguir mostra os resultados da classificação por localA[localIdx[0]].
Resultados da ordenaçãoVocê pode exportar o conteúdo na janela Inspeção paralela para o Excel escolhendo o botão Excel e, em seguida, escolhendo Abrir no Excel. Se você tiver o Excel instalado no computador de desenvolvimento, o botão abrirá uma planilha do Excel que contém o conteúdo.
No canto superior direito da janela Inspeção paralela , há um controle de filtro que você pode usar para filtrar o conteúdo usando expressões booleanas. Entre
localA[localIdx[0]] > 20000na caixa de texto de controle de filtro e escolha a tecla Enter .A janela agora contém apenas threads nos quais o valor
localA[localIdx[0]]é maior que 20000. O conteúdo ainda é classificado pelalocalA[localIdx[0]]coluna, que é a ação de classificação que você escolheu anteriormente.
Sinalizando threads de GPU
Você pode marcar threads de GPU específicos sinalizando-os na janela GPU Threads , na janela Parallel Watch ou na DataTip na janela Parallel Stacks . Se uma linha na janela GPU Threads contiver mais de um thread, sinalizar essa linha sinalizará todos os threads contidos na linha.
Para sinalizar threads de GPU
Selecione o cabeçalho da coluna [Thread] na janela Parallel Watch 1 para classificar por índice de bloco e índice de thread.
Na barra de menus, escolha Debug>Continue, que faz com que os quatro threads que estavam ativos progridam para a próxima barreira (definida na linha 32 de AMPMapReduce.cpp).
Escolha o símbolo de bandeira no lado esquerdo da linha que contém os quatro tópicos que estão ativos agora.
A ilustração a seguir mostra os quatro threads sinalizados ativos na janela GPU Threads .
Threads ativos na janela de GPU ThreadsA janela Parallel Watch e a DataTip da janela Parallel Stacks indicam os threads sinalizados.
Se quiser se concentrar nos quatro threads sinalizados, você pode optar por mostrar apenas os threads sinalizados. Ele limita o que você vê nas janelas GPU Threads, Parallel Watch e Parallel Stacks .
Escolha o botão Mostrar Apenas Marcados em qualquer uma das janelas ou na barra de ferramentas Local da Depuração. A ilustração a seguir mostra o botão Mostrar Apenas Sinalizado na barra de ferramentas Local de Depuração.
Botão Mostrar Apenas SinalizadoAgora, as janelas GPU Threads, Parallel Watch e Parallel Stacks exibem apenas os threads sinalizados.
Congelamento e descongelamento de threads de GPU
Você pode congelar (suspender) e descongelar (retomar) threads da GPU na janela GPU Threads ou na janela Parallel Watch . Você pode congelar e descongelar threads da CPU da mesma maneira; para obter informações, consulte Como usar a janela Threads.
Para congelar e descongelar threads da GPU
Escolha o botão Mostrar somente sinalizado para exibir todos os threads.
Na barra de menus, escolha Depurar>continuar.
Abra o menu de atalho para a linha ativa e escolha Congelar.
A ilustração a seguir da janela GPU Threads mostra que todos os quatro threads estão congelados.
Threads congelados na janela de GPU ThreadsDa mesma forma, a janela Parallel Watch mostra que todos os quatro threads estão congelados.
Na barra de menus, escolha Depurar>continuar para permitir que os próximos quatro threads da GPU progridam além da barreira na linha 22 e atinjam o ponto de interrupção na linha 30. A janela GPU Threads mostra que os quatro threads anteriormente congelados permanecem congelados e no estado ativo.
Na barra de menus, escolha Depurar, Continuar.
Na janela Monitorização paralela , você também pode descongelar threads de GPU individuais ou múltiplos.
Para agrupar threads de GPU
No menu de atalho para um dos threads na janela GPU Threads , escolha Agrupar por, Endereço.
Os threads na janela GPU Threads são agrupados por endereço. O endereço corresponde à instrução de desmontagem onde cada grupo de threads está localizado. 24 threads estão na linha 22 onde o método tile_barrier::wait é executado. 12 fios estão na instrução para a barreira na linha 32. Quatro dessas threads estão marcadas. Oito threads estão no ponto de interrupção na linha 30. Quatro desses fios estão congelados. A ilustração a seguir mostra os threads agrupados na janela GPU Threads .
Os threads agrupados na janela GPU ThreadsVocê também pode fazer a operação Agrupar por abrindo o menu de atalho para a grade de dados da janela Inspeção paralela . Selecione Agrupar por e, em seguida, escolha o item de menu que corresponde à forma como você deseja agrupar os threads.
Executando todos os encadeamentos para um ponto específico no código
Execute todos os processos em um determinado bloco até à linha que contém o cursor usando Executar bloco atual até ao cursor.
Para executar todos os threads no local marcado pelo cursor
No menu de atalho para os fios congelados, escolha Descongelar.
No Editor de Códigos, coloque o cursor na linha 30.
No menu de atalho do Editor de Códigos, escolha Executar bloco atual para cursor.
Os 24 fios que antes estavam bloqueados na barreira da linha 21 progrediram para a linha 32. É mostrado na janela GPU Threads .
Ver também
Visão geral do C++ AMP
Depurando o código da GPU
Como usar a janela de Tarefas de GPU
Como: Usar a janela de observação paralela
Analisando o código AMP C++ com o Visualizador de Concorrência