C ++

Programação GPU com C ++

Programação GPU com C ++

Visão geral

Neste guia, exploraremos o poder da programação de GPU com C++. Os desenvolvedores podem esperar um desempenho incrível com C ++, e acessar o poder fenomenal da GPU com uma linguagem de baixo nível pode render alguns dos cálculos mais rápidos disponíveis atualmente.

Requisitos

Embora qualquer máquina capaz de executar uma versão moderna do Linux possa suportar um compilador C ++, você precisará de uma GPU baseada em NVIDIA para acompanhar este exercício. Se você não tem uma GPU, pode ativar uma instância com GPU no Amazon Web Services ou em outro provedor de nuvem de sua escolha.

Se você escolher uma máquina física, certifique-se de ter os drivers proprietários NVIDIA instalados. Você pode encontrar instruções para isso aqui: https: // linuxhint.com / install-nvidia-drivers-linux /

Além do driver, você precisará do kit de ferramentas CUDA. Neste exemplo, usaremos o Ubuntu 16.04 LTS, mas há downloads disponíveis para a maioria das principais distribuições no seguinte URL: https: // desenvolvedor.nvidia.com / cuda-downloads

Para o Ubuntu, você escolheria o .download baseado em deb. O arquivo baixado não terá um .extensão deb por padrão, então eu recomendo renomeá-la para ter um .deb no final. Então, você pode instalar com:

sudo dpkg -i nome do pacote.deb

Provavelmente, você será solicitado a instalar uma chave GPG e, em caso afirmativo, siga as instruções fornecidas para fazer isso.

Depois de fazer isso, atualize seus repositórios:

sudo apt-get update
sudo apt-get install cuda -y

Uma vez feito isso, recomendo reiniciar para garantir que tudo seja carregado corretamente.

Os benefícios do desenvolvimento de GPU

As CPUs lidam com muitas entradas e saídas diferentes e contêm uma grande variedade de funções, não apenas para lidar com uma grande variedade de necessidades do programa, mas também para gerenciar várias configurações de hardware. Eles também lidam com memória, cache, barramento do sistema, segmentação e funcionalidade IO, tornando-os um pau para toda obra.

As GPUs são o oposto - elas contêm muitos processadores individuais que se concentram em funções matemáticas muito simples. Por causa disso, eles processam tarefas muitas vezes mais rápido do que CPUs. Ao se especializarem em funções escalares (uma função que recebe uma ou mais entradas, mas retorna apenas uma única saída), eles alcançam um desempenho extremo ao custo de uma especialização extrema.

Código de exemplo

No código de exemplo, adicionamos vetores juntos. Eu adicionei uma versão de CPU e GPU do código para comparação de velocidade.
exemplo de gpu.cpp conteúdo abaixo:

#include "cuda_runtime.h "
#incluir
#incluir
#incluir
#incluir
#incluir
typedef std :: chrono :: high_resolution_clock Clock;
# define ITER 65535
// Versão da CPU da função de adição de vetor
void vector_add_cpu (int * a, int * b, int * c, int n)
int i;
// Adicione os elementos do vetor aeb ao vetor c
para (i = 0; i < n; ++i)
c [i] = a [i] + b [i];


// Versão GPU da função de adição de vetor
__global__ void vector_add_gpu (int * gpu_a, int * gpu_b, int * gpu_c, int n)
int i = threadIdx.x;
// Não é necessário o loop for porque o tempo de execução CUDA
// irá encadear este ITER vezes
gpu_c [i] = gpu_a [i] + gpu_b [i];

int main ()
int * a, * b, * c;
int * gpu_a, * gpu_b, * gpu_c;
a = (int *) malloc (ITER * sizeof (int));
b = (int *) malloc (ITER * sizeof (int));
c = (int *) malloc (ITER * sizeof (int));
// Precisamos de variáveis ​​acessíveis para a GPU,
// então cudaMallocManaged fornece esses
cudaMallocManaged (& gpu_a, ITER * sizeof (int));
cudaMallocManaged (& gpu_b, ITER * sizeof (int));
cudaMallocManaged (& gpu_c, ITER * sizeof (int));
para (int i = 0; i < ITER; ++i)
a [i] = i;
b [i] = i;
c [i] = i;

// Chame a função CPU e cronometre-a
auto cpu_start = Clock :: now ();
vector_add_cpu (a, b, c, ITER);
auto cpu_end = Clock :: now ();
std :: cout << "vector_add_cpu: "
<< std::chrono::duration_cast(cpu_end - cpu_start).contar()
<< " nanoseconds.\n";
// Chame a função GPU e cronometre-a
// Os freios de ângulo triplo são uma extensão de tempo de execução CUDA que permite
// parâmetros de uma chamada de kernel CUDA a serem passados.
// Neste exemplo, estamos passando um bloco de thread com threads ITER.
auto gpu_start = Clock :: now ();
vector_add_gpu <<<1, ITER>>> (gpu_a, gpu_b, gpu_c, ITER);
cudaDeviceSynchronize ();
auto gpu_end = Clock :: now ();
std :: cout << "vector_add_gpu: "
<< std::chrono::duration_cast(gpu_end - gpu_start).contar()
<< " nanoseconds.\n";
// Libera as alocações de memória baseadas em função GPU
cudaFree (a);
cudaFree (b);
cudaFree (c);
// Libere as alocações de memória baseadas em função da CPU
livre (a);
livre (b);
livre (c);
return 0;

Makefile conteúdo abaixo:

INC = -I / usr / local / cuda / incluir
NVCC = / usr / local / cuda / bin / nvcc
NVCC_OPT = -std = c ++ 11
tudo:
$ (NVCC) $ (NVCC_OPT) exemplo de gpu.cpp -o gpu-example
limpar:
-rm -f gpu-example

Para executar o exemplo, compile-o:

faço

Em seguida, execute o programa:

./ gpu-example

Como você pode ver, a versão da CPU (vector_add_cpu) é consideravelmente mais lenta do que a versão da GPU (vector_add_gpu).

Caso contrário, pode ser necessário ajustar a definição de ITER no exemplo de gpu.cu para um número maior. Isso ocorre porque o tempo de configuração da GPU é mais longo do que alguns loops menores com uso intensivo de CPU. Descobri que 65535 funciona bem na minha máquina, mas sua milhagem pode variar. No entanto, depois de ultrapassar esse limite, a GPU é dramaticamente mais rápida do que a CPU.

Conclusão

Espero que você tenha aprendido muito com nossa introdução à programação de GPU com C++. O exemplo acima não é muito útil, mas os conceitos demonstrados fornecem uma estrutura que você pode usar para incorporar suas ideias para liberar o poder de sua GPU.

Como reverter a direção de rolagem do Mouse e Touchpads no Windows 10
Mouse e Touchpads não apenas torna a computação mais fácil, mas mais eficiente e menos demorada. Não podemos imaginar uma vida sem esses dispositivos,...
Como alterar o ponteiro do mouse e tamanho do cursor, cor e esquema no Windows 10
O ponteiro do mouse e o cursor no Windows 10 são aspectos muito importantes do sistema operacional. Isso também pode ser dito para outros sistemas ope...
Mecanismos de jogos gratuitos e de código aberto para o desenvolvimento de jogos Linux
Este artigo cobrirá uma lista de mecanismos de jogo gratuitos e de código aberto que podem ser usados ​​para desenvolver jogos 2D e 3D no Linux. Exist...