Skip to content

👁️‍🗨️📗 It's a repository of Nvidia CUDA programming.

Notifications You must be signed in to change notification settings

IsaacAlves7/nvidia-cuda

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 

Repository files navigation

👁️‍🗨️ Nvidia CUDA

O Nvidia CUDA é uma plataforma de computação paralela e um modelo de programação para ser usado com GPUs - Unidade de Processamento Gráfica. Com ele, toda a parte “pesada” do código é executada nos diversos núcleos da placa de vídeo enquanto apenas a parte sequencial do código é executada no processador, obtendo um ganho significativo de performance.

CUDA anteriormente conhecida como Arquitetura de Dispositivo de Computação Unificada é uma API destinada a computação paralela.

O uso da GPU tem custos indiretos. Se o cálculo não for pesado o suficiente, o custo (em tempo) de usar uma GPU pode ser maior do que o ganho. Por outro lado, se o cálculo for pesado, você pode ver uma grande melhoria na velocidade.

Vários termos importantes no tópico de programação CUDA estão listados aqui:

  • Host: a CPU
  • Device: a GPU
  • Host memory: a memória principal do sistema
  • Device memory: memória integrada em um cartão GPU
  • Kernel: uma função GPU lançada pelo host e executada no dispositivo
  • Device function: uma função GPU executada no dispositivo que só pode ser chamada a partir do dispositivo (ou seja, a partir de um kernel ou outra função do dispositivo).

O Numba oferece suporte à programação de GPU CUDA compilando diretamente um subconjunto restrito de código Python em kernels CUDA e funções de dispositivo seguindo o modelo de execução CUDA. Kernels escritos em Numba parecem ter acesso direto aos arrays NumPy. Matrizes NumPy são transferidas entre a CPU e a GPU automaticamente. Numba funciona permitindo que você especifique assinaturas de tipo para funções Python, o que permite a compilação em tempo de execução (isto é, “Just-in-Time” ou compilação JIT).

Numba é um projeto de código aberto, licenciado por BSD, que se baseia fortemente nas capacidades do compilador LLVM.

Exemplo: O decorador @vectorize, no código a seguir, gera uma versão compilada e vetorizada da função escalar em tempo de execução para que possa ser usada para processar matrizes de dados em paralelo na GPU.

import numpy as np
from numba import vectorize

@vectorize(['float32(float32, float32)'], target='cuda')
def Add(a, b):
return a + b

# Initialize arrays
N = 100000
A = np.ones(N, dtype=np.float32)
B = np.ones(A.shape, dtype=A.dtype)
C = np.empty_like(A, dtype=A.dtype)

# Add arrays on GPU
C = Add(A, B)

Para compilar e executar a mesma função na CPU, simplesmente mudamos o destino para 'cpu', o que produz desempenho no nível do código C vetorizado e compilado na CPU. Essa flexibilidade ajuda a produzir código mais reutilizável e permite desenvolver em máquinas sem GPUs.

Um dos pontos fortes da plataforma de computação paralela CUDA é a variedade de bibliotecas aceleradas por GPUs disponíveis.

Outro projeto da equipe Numba, chamado pyculib, fornece uma interface Python para as bibliotecas CUDA cuBLAS (álgebra linear densa), cuFFT (Fast Fourier Transform) e cuRAND (geração de número aleatório).

Muitos aplicativos serão capazes de obter uma aceleração significativa apenas usando essas bibliotecas, sem escrever nenhum código específico da GPU. Por exemplo, o código a seguir gera um milhão de números aleatórios uniformemente distribuídos na GPU usando o gerador de números pseudoaleatórios “XORWOW”.

import numpy as np
from pyculib import rand as curand

prng = curand.PRNG(rndtype=curand.PRNG.XORWOW)
rand = np.empty(100000)
prng.uniform(rand)
print rand[:10]

A capacidade do Numba de compilar código dinamicamente significa que você não abre mão da flexibilidade do Python. Esse é um grande passo para fornecer a combinação ideal de programação de alta produtividade e computação de alto desempenho.

O back-end da GPU do Numba utiliza o NVIDIA Compiler SDK baseado em LLVM. Os wrappers pyculib em torno das bibliotecas CUDA também são de código aberto e licenciados por BSD.

Para começar a usar o Numba, a primeira etapa é baixar e instalar a distribuição Anaconda Python, uma "distribuição Python totalmente gratuita, pronta para empresas, para processamento de dados em grande escala, análise preditiva e computação científica" que inclui muitos pacotes populares (Numpy, SciPy, Matplotlib, IPython etc).

Digite o comando para baixar o Numba:

apt-get install libgl1-mesa-glx libegl1-mesa libxrandr2 libxrandr2 libxss1 libxcursor1 libxcomposite1 libasound2 libxi6 libxtst6

Agora, você pode ativar a instalação, fazendo um source no arquivo ~/.bashrc: source ~/.bashrc

Assim que tiver feito isso, você será levado ao ambiente de programação padrão de base do Anaconda, e seu prompt de comando mudará para o seguinte: (base) summy@ubuntu:~$

Embora o Anaconda venha com esse ambiente de programação padrão de base, você deve criar ambientes separados para seus programas e mantê-los isolados um do outro. Você pode, ainda, verificar sua instalação fazendo o uso do comando conda, por exemplo, com list:

conda list

Você receberá a saída de todos os pacotes disponíveis através da instalação do Anaconda.

# packages in environment at /home/sammy/anaconda3:
# Name                    Version                  Build  Channel
_ipyw_jlab_nb_ext_conf    0.1.0                    py37_0
_libgcc_mutex             0.1                        main
alabaster                 0.7.12                   py37_0
anaconda                  2020.02                  py37_0
...

Agora que o Anaconda está instalado, podemos seguir em frente para a configuração dos ambientes dele.

Atenção: Os ambientes virtuais do Anaconda lhe permitem manter projetos organizados pelas versões do Python e pelos pacotes necessários. Para cada ambiente do Anaconda que você configurar, especifique qual versão do Python usar e mantenha todos os arquivos de programação relacionados dentro desse diretório.

Primeiro, podemos verificar quais versões do Python estão disponíveis para que possamos usar: conda search "^python$"

Vamos criar um ambiente usando a versão mais recente do Python 3.

Podemos conseguir isso atribuindo a versão 3 ao argumento python. Vamos chamar o ambiente de my_env, mas você pode usar um nome mais descritivo para o ambiente, especialmente se estiver usando ambientes para acessar mais de uma versão do Python.

conda create --name my_env python=3

Você receberá uma saída com informações sobre o que está baixado e quais pacotes serão instalados e, em seguida, será solicitado a prosseguir com y ou n. Assim que concordar, digite y.

O utilitário conda agora irá obter os pacotes para o ambiente e informá-lo assim que estiver concluído. Você pode ativar seu novo ambiente digitando o seguinte:

conda activate my_env

Com seu ambiente ativado, seu prefixo do prompt de comando irá refletir que você não está mais no ambiente base, mas no novo ambiente que acabou de criar.

(my_env) summy@ubuntu:~$

Dentro do ambiente, você pode verificar se está usando a versão do Python que tinha intenção de usar: (my_env) summy@ubuntu:~$ python –version

Quando estiver pronto para desativar seu ambiente do Anaconda, você pode fazer isso digitando: (my_env) summy@ubuntu:~$ conda deactivate

Observe que pode substituir a palavra source por . para obter os mesmos resultados. Para focar em uma versão mais específica do Python, você pode passar uma versão específica para o argumento python, como 3.5, por exemplo:

conda create -n my_env35 python=3.5

Você pode inspecionar todos os ambientes que configurou com este comando:

(base) summy@ubuntu:~$ conda info –envs


# conda environments:
#
base                  *  /home/sammy/anaconda3
my_env                   /home/sammy/anaconda3/envs/my_env
my_env35                 /home/sammy/anaconda3/envs/my_env35

O asterisco indica o ambiente ativo atual. Cada ambiente que você criar com o conda create virá com vários pacotes padrão:

  • _libgcc_mutex
  • ca-certificates
  • certifi
  • libedit
  • libffi
  • libgcc-ng
  • libstdcxx-ng
  • ncurses
  • openssl
  • pip
  • python
  • readline
  • setuptools
  • sqlite
  • tk
  • wheel
  • xz
  • zlib

Você pode acrescentar pacotes adicionais, como o Numpy, por exemplo, com o seguinte comando:

conda install --name my_env35 numpy

Se você já sabe que gostaria de um ambiente Numpy após a criação, pode concentrá-lo em seu comando conda create:

conda create --name my_env python=3 numpy

Se você não estiver mais trabalhando em um projeto específico e não tiver mais necessidade do ambiente associado, pode removê-lo. Para fazer isso, digite o seguinte:

conda remove --name my_env35 --all

Atenção: Agora, quando você digitar o comando conda info --envs, o ambiente que removeu não será mais listado.

Você deve garantir regularmente que o Anaconda esteja atualizado para que você esteja trabalhando com todas as versões mais recentes do pacote. Para fazer isso, deve primeiro atualizar o utilitário conda: (base) summy@ubuntu:~$ conda update conda

Quando solicitado a fazer isso, digite y para continuar com a atualização. Assim que a atualização do conda estiver concluída, você pode atualizar a distribuição do Anaconda:

conda update anaconda

Atenção: Novamente, quando solicitado a fazer isso, digite y para continuar. Isso garantirá que você esteja usando as versões mais recentes do conda e do Anaconda.

Depois de instalar o Anaconda, instale os pacotes CUDA necessários digitando:

conda install numba cudatoolkit pyculib

O Anaconda (anteriormente Continuum Analytics) reconheceu que alcançar grandes acelerações em alguns cálculos requer uma interface de programação mais expressiva com controle mais detalhado sobre o paralelismo do que as bibliotecas e a vetorização automática de loop podem fornecer.

Portanto, o Numba possui outro conjunto importante de recursos que constitui o que é conhecido não oficialmente como “CUDA Python”.

Numba expõe o modelo de programação CUDA, assim como em CUDA C / C ++, mas usando a sintaxe Python pura, para que os programadores possam criar kernels paralelos personalizados e ajustados sem deixar o conforto e as vantagens do Python para trás. O CUDA JIT da Numba (disponível via decorador ou chamada de função) compila funções CUDA Python em tempo de execução, especializando-as para os tipos que você usa, e sua API CUDA Python fornece controle explícito sobre transferências de dados e fluxos CUDA, entre outros recursos.

O exemplo de código a seguir demonstra isso com um kernel de conjunto Mandelbrot simples. Observe que a função mandel_kernel usa as estruturas cuda.threadIdx, cuda.blockIdx, cuda.blockDim e cuda.gridDim fornecidas por Numba para calcular os índices globais de pixel X e Y para o segmento atual. Como em outras linguagens CUDA, lançamos o kernel inserindo uma "configuração de execução" (linguagem CUDA para o número de threads e blocos de threads a serem usados para executar o kernel) entre colchetes, entre o nome da função e a lista de argumentos: mandel_kernel [griddim, blockdim] (- 2.0, 1.0, -1.0, 1.0, d_image, 20). Você também pode ver o uso das funções de API to_host e to_device para copiar dados de e para a GPU.

@cuda.jit
def mandel_kernel(min_x, max_x, min_y, max_y, image, iters):
	height = image.shape[0]
	width = image.shape[1]

	pixel_size_x = (max_x - min_x) / width
	pixel_size_y = (max_y - min_y) / height

	startX = cuda.blockDim.x * cuda.blockIdx.x + cuda.threadIdx.x
	startY = cuda.blockDim.y * cuda.blockIdx.y + cuda.threadIdx.y
	gridX = cuda.gridDim.x * cuda.blockDim.x;
	gridY = cuda.gridDim.y * cuda.blockDim.y;

	for x in range(startX, width, gridX):
		real = min_x + x * pixel_size_x
		for y in range(startY, height, gridY):
			imag = min_y + y * pixel_size_y
			image[y, x] = mandel(real, imag, iters)

	gimage = np.zeros((1024, 1536), dtype = np.uint8)
	blockdim = (32, 8)
	griddim = (32,16)

	start = timer()
	d_image = cuda.to_device(gimage)
	mandel_kernel[griddim, blockdim](-2.0, 1.0, -1.0, 1.0, d_image, 20)
	d_image.to_host()
	dt = timer() - start

	print "Mandelbrot created on GPU in %f s" % dt

	imshow(gimage)

Em um servidor com uma GPU NVIDIA Tesla P100 e uma CPU Intel Xeon E5-2698 v3, este código CUDA Python Mandelbrot é executado quase 1700 vezes mais rápido do que a versão Python pura. 1700x pode parecer uma aceleração irreal, mas tenha em mente que estamos comparando o código Python compilado, paralelo e acelerado por GPU ao código Python interpretado de thread único na CPU.

About

👁️‍🗨️📗 It's a repository of Nvidia CUDA programming.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published