Passo a passo: Depuração de um aplicativo de AMP C++
Este tópico demonstra como depurar um aplicativo que usa o Paralelismo Maciço Acelerado do C++ (AMP C++, sigla em inglês) para aproveitar a unidade de processamentos gráficos (GPU).Ele usa um programa de redução paralela que resume uma grande matriz de inteiros.Essa explicação passo a passo mostra as seguintes tarefas:
Iniciando o depurador do GPU.
Inspecionando threads do GPU na janela de Threads do GPU.
Usando a janela de Pilhas Paralelas para observar simultaneamente as pilhas de chamada de vários threads do GPU.
Usando a janela Relógio Paralela para verificar valores de uma única expressão por vários threads ao mesmo tempo.
Sinalizando, congelando, descongelando e agrupando threads do GPU.
Executando todos os segmentos de um quadro para um local específico no código.
Pré-requisitos
Antes de iniciar esta explicação passo a passo:
Leia Visão geral do C++ AMP.
Certifique-se de que o número de linhas é exibido no editor de texto.Para obter mais informações, consulte Como: exibir números de linha no Editor.
Certifique-se de que você está executando o Windows 8 ou Windows Server 2012 para oferecer suporte 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ê possui e as configurações que você usa determinam esses elementos. Para obter mais informações, consulte Configurações de Visual Studio. |
Para criar o projeto modelo
Inicie o Visual Studio.
Na barra de menu, escolha Arquivo, Novo, Projeto.
Em baixo de Instalado no painel de modelos, escolha Visual C++.
Escolha Aplicativo de Console do Win32, digite AMPMapReduce na caixa de Nome , e então escolha o botão OK.
Escolha o botão Próximo.
Desmarque a caixa de seleção Cabeçalho pré-compilado e então escolha o botão Concluir.
Em Gerenciador de Soluções, delete stdafx.h, targetver.h, e stdafx.cpp do projeto.
Abra AMPMapReduce.cpp e substitua seu conteúdo com o 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 menu, escolha Arquivo, Salvar Tudo.
No Gerenciador de Soluções, abra o menu de atalho para AMPMapReduce, e então escolha Propriedades.
Na caixa de diálogo Páginas de Propriedade , em baixo de Propriedades de Configuração, escolha C/C++, Cabeçalhos Pré-Compilados.
Para a propriedade Cabeçalho Pré-compilado, selecione Não Usar Cabeçalhos Pré-Compiladose então clique no botão OK.
Na barra de menu, escolha Compilar, Compilar Solução.
Depurando o Código da CPU
Nsse procedimento, você usará o Depurador Local do Windows para certificar-se que o código da CPU neste aplicativo está correto.O segmento do código da CPU neste aplicativo que é especialmente interessante é o laço for na função reduction_sum_gpu_kernel.Ele controla a redução paralela baseada em árvore que é realizada no GPU.
Para depurar o Código da CPU
No Gerenciador de Soluções, abra o menu de atalho para AMPMapReduce, e então escolha Propriedades.
Na caixa de diálogo Páginas de Propriedade, em baixo de Propriedades de Configuração, escolha Depurando.Verifique que Depurador Local do Windows está selecionado na lista Depurador a iniciar.
Retorne ao editor de códigos.
Defina pontos de interrupção nas linhas de código mostradas na ilustração a seguir (aproximadamente linhas 67 a 70).
pontos de interrupção da CPU
Na barra de menu, escolha Depurar, Iniciar Depuração.
Na janela de Locais , observe o valor para stride_size até que o ponto de interrupção na linha 70 é alcançado.
Na barra de menu, 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 função sum_kernel_tiled.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 então escolha Propriedades.
Na caixa de diálogo Páginas de Propriedade, em baixo de Propriedades de Configuração, escolha Depurando.
Na lista Depurador a iniciar, selecione Depurador Local do Windows.
Na lista Tipo de Depurador, selecione Somente GPU.
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 GPU
Na barra de menu, 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 de Threads da GPU
Para abrir a janela de Threads da GPU, na barra de menu, escolha Depurar, Janelas, Threads da GPU.
Você pode inspecionar o estado dos threads da GPU na janela de Threads da GPU que aparece.
Encaixe a janela de Threads da GPU na parte inferior do Visual Studio.Escolha o botão Expandir Alternância de Thread para exibir as caixas de texto do bloco e do thread.A janela de Threads da GPU mostra o número total de threads ativos e bloqueados da GPU, como mostrado na ilustração a seguir:
Janela de Threads da GPU
Existem 313 blocos alocados para esse cálculo.Cada bloco contém 32 threads.Porque a depuração local da GPU ocorre em um emulador de software, há quatro threads ativos da GPU.Os quatro threads executam as instruções simultaneamente e então seguem juntos para a próxima instrução.
Na janela de threads da GPU, há quatro threads da GPU ativos e 28 threads da GPU bloqueados na declaraçã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 o thread que será alternado na janela de Threads da GPU, abra o menu de atalho e escolha Alternar Para Thread.Se a linha representa mais de um thread, você alternará para o primeiro thread de acordo com as coordenadas do thread.
Insira valores do bloco e do thread do thread nas caixas de texto correspondente e então escolha o botão Alternar Thread.
A janela da pilha de chamadas exibe a pilha de chamadas do thread atual da GPU.
Para usar a janela de pilhas paralelas
Para abrir a janela de pilhas paralelas, na barra de menu, escolha Depurar, Janelas, Pilhas paralelas.
Você pode usar a janela de pilhas paralels para inspecionar simultaneamente os quadros de pilha de vários threads da GPU.
Encaixe a janela de 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 de Pilhas paralelas mostra uma exibição focada da pilha de chamadas dos threads da GPU que você viu na janela de Threads da GPU.
Janela de Pilhas Paralelas
32 threads foram do _kernel_stub para a instrução lambda na chamada de função parallel_for_each e então para a função sum_kernel_tiled, onde a redução paralela ocorre.28 de 32 threads progrediram para a declaração tile_barrier::wait e permanecem bloqueados na linha 22, enquanto os outros 4 threads permanecem ativos na função sum_kernel_tiled na linha 30.
Você pode inspecionar as propriedades de um thread da GPU que estão disponíveis na janela de threads da GPU no DataTip rico da janela de pilhas paralelas.Para fazer isso, posicione o ponteiro do mouse no quadro de pilha do sum_kernel_tiled.A ilustração a seguir mostra o DataTip.
DataTip do thread da GPU
Para obter mais informações sobre a janela de pilhas paralelas, consulte Usando a janela de pilhas paralela.
Para usar a janela de inspeção paralela
Para abrir a janela de inspeção paralela, na barra de menu, escolha Depurar, Janelas, Inspeção Paralela, inspeção paralela 1.
Você pode usar a janela de inspeção paralela para inspecionar os valores de uma expressão atravéz de vários threads.
Encaixe a janela de inspeção paralela 1 na parte inferior do Visual Studio.Existem 32 linhas na tabela da janela de inspeção paralela.Cada um corresponde a um thread da GPU que aparece na janela de Threads da GPU e na janela de pilhas de paralela.Agora, você pode inserir expressões cujos valores você deseja inspecionar por todos os 32 threads da GPU.
Selecione o cabeçalho da coluna Adicionar Inspeção, digite localIdx, e pressione ENTER.
Selecione, novamente, o cabeçalho da coluna Adicionar Inspeção, digite globalIdx, e pressione ENTER.
Selecione, novamente, o cabeçalho da coluna Adicionar Inspeção, digite localA[localIdx[0]], e pressione ENTER.
Você pode classificar por uma expressão específica 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 do tipo
Você pode exportar o conteúdo na janela de Inpeção Paralela para o excel escolhendo o botão do excel e então Abra no excel.Se você tiver o Excel instalado no seu computador de desenvolvimento, isso abre uma planilha do excel com o conteúdo.
Em o canto superior direito da janela de Inpeção Paralela, há um controle de filtro que você pode usar para filtrar o conteúdo usando expressões booleanas.Digite localA[localIdx[0]] > 20000 na caixa de texto do controle de filtro e pressione ENTER.
A janela contém agora somente os threads em que o valor de localA[localIdx[0]] é maior que 20000.O conteúdo ainda é classificado pela coluna de localA[localIdx[0]] , que é a ação de classificação que você executou anteriormente.
Sinalizando Threads da GPU
Você pode marcar threads específicos da GPU sinalizando-os na janela de Threads da GPU, na janela de Inspeção Paralela, ou no DataTip na janela de Pilhas Paralelas.Se uma linha na janela de Threads da GPU contém mais de um thread, sinalizar essa linha sinaliza todos os threads que estão contidos na linha.
Para sinalizar threads da GPU
Selecione o cabeçalho da coluna do [Thread] na janela de Inpeção Paralela 1 para classificar por índice de bloco e índice de thread.
Na barra de menu, escolha Depurar, Continuar, o que faz com que os quatro threads que estão ativos progridam para a barreira seguinte (definida na linha 32 do AMPMapReduce.cpp).
Escolha o símbolo do sinalizador no lado esquerdo da linha que contém os quatro threads que agora estão ativos.
A ilustração a seguir mostra os quatro threads assinalados ativos na janela de threads da GPU.
Threads ativos na janela de Threads da GPU
A janela de Inpeção paralela e o DataTip da janela de pilhas de Paralelas indicam, ambos, os threads assinalados.
Se você deseja se concentra nos quatro threads que você sinalizou, você pode escolher para mostrar, nos threads da GPU, na Inpeção paralela, e na janela de pilhas paralelas, somente os threads assinalados.
Pressione o botão mostrar apenas sinalizado em qualquer uma das janelas ou na barra de ferramentas Depurar Localização.A ilustração a seguir mostra o botão mostrar apenas sinalizado na barra de ferramentas Depurar Localização.
Botão Mostrar Apenas Sinalizado
Agora as janelas de Threads da GPU, Inspeção Paralela, e de Pilhas Paralelas exibem somente os threads assinalados.
Congelando e Descongelando threads da GPU
Você pode congelar (Suspender) e descongelar (Continuar) threads da GPU a partir da janela Threads da GPU ou da janela de Inspeção Paralela.Você pode congelar e descongelar threads da CPU da mesma forma; para mais informações, consulte Como: usar a janela Threads.
Para congelar e descongelar threads da GPU
Escolha o botão Mostrar Apenas Sinalizado para exibir todos os threads.
Na barra de menu, escolha Depurar, Continuar.
Abra o menu de atalho para a linha ativa e então escolha Congelar.
A ilustração a seguir da janela de threads da GPU mostra que todos os quatro threads estão congelados.
Threads congelados na janela de Threads da GPU
Da mesma forma, a janela de Inspeção Paralela mostra que todos os quatro threads estão congelados.
Na barra de menu, escolha Depurar, Continuar para permitir aos próximos quatro threads da GPU progredir após a barreira na linha 22 e atingir o ponto de interrupção na linha 30.A janela de Threads da GPU mostra que os quatro threads congelados anteriormente permanecem congelados e em estado ativo.
Na barra de menu, escolha Depurar, Continuar.
Da janela de Inpeção Paralela, você também pode descongelar threads individuais ou múltiplos da GPU.
Para agrupar threads da GPU
No menu de atalho para um dos threads na janela Threads da GPU , escolha Agrupar Por, Endereço.
Os threads na janela de Threads da GPU são agrupados por endereço.O endereço corresponde à instrução em desmontagem onde cada grupo de threads é localizado.24 threads estão na linha 22, onde o Método de tile_barrier::wait é executado.12 threads estão na instrução para a barreira na linha 32.Quatro desses threads são sinalizados.Oito threads estão no ponto de interrupção na linha 30.Quatro desses threads estão congelados.A ilustração a seguir mostra os threads agrupados na janela de Threads da GPU.
Threads agrupados na janela de Threads da GPU
Você também pode executar a operação Agrupar por abrindo o menu de atalho para a grade de dados da janela de Inspeção Paralela, escolhendo Agrupar por e escolhendo o item de menu que corresponde a como você deseja agrupar os threads.
Executando Todos os Threads para uma Localização Específica no Código
Você executa todos os threads em um determinado quadro para a linha que contém o cursor usando Executar Bloco Atual para o Cursor.
Para executar todos os threads para o local marcado pelo cursor
No menu de atalho para os threads congelados, escolha Descongelar.
No editor de códigos, coloque o cursor na linha 30.
No menu de atalho para o Editor de Códigos, escolha Executar Bloco Atual para o Cursor.
Os 24 threads que foram previamente bloqueados na barreira na linha 21 progrediram para a linha 32.Isso é mostrado na janela Threads da GPU.
Consulte também
Tarefas
Como: usar a janela de Threads GPU
Como: usar a janela de inspeção paralela