diff --git a/README.md b/README.md index 9f7d556..e539861 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Ao final do curso os participantes deverão ser capazes de: - Os alunos deverão assistir às aulas, ler o material disponibilizado, participar nos fóruns de discussão, responder aos questionários e implementar os programas solicitados, incluindo um projeto final. Apresentação presencial de um projeto ao final do curso. ## RECURSOS A SEREM UTILIZADOS: -Ambiente virtual de aprendizado, Vídeo-aulas, textos e documentação das ferramentas usadas, ambientes de desenvolvimento C/C++ em qualquer sistema operacional. +Ambiente virtual de aprendizado, Vídeo-aulas, textos e documentação das ferramentas usadas, ambientes de desenvolvimento C/C++ em qualquer sistema operacional... ## [Playlist das aulas no YouTube](https://www.youtube.com/playlist?list=PLezQJVF86FUulrCIovlqO-cbs-Uw-LtIo) diff --git a/code/CUDA/CUDA.ipynb b/code/CUDA/CUDA.ipynb index 41dda6e..7a2cab3 100644 --- a/code/CUDA/CUDA.ipynb +++ b/code/CUDA/CUDA.ipynb @@ -1,1854 +1 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "uBl_9yStW0cz" - }, - "source": [ - "# Introdução ao CUDA\n", - "\n", - "Esta pode ser considerada uma introdução mínima ao CUDA e às possibilidades de uso neste ambiente. \n", - "\n", - "---\n", - "## Configurando e instalando \n", - "\n", - "Para acessar GPUs NVIDIA no Google Colab é preciso alterar a configuração do ambiente. Para isso, clique no menu superior em **Runtime > Change runtime type**, em **Hardware accelerator** selecione **GPU** e clique em **Save**. Esta configuração é persistente, ou seja, o arquivo já será inicializado neste ambiente da próxima vez que for aberto. \n", - "\n", - "O compilador CUDA `nvcc` já vem instalado e pode ser invocado como vemos abaixo, solicitando sua versão com o parâmetro `--version`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 1063, - "status": "ok", - "timestamp": 1667307703978, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "rPPSIrkTVO35", - "outputId": "3af38541-0a94-4236-d6f9-c460fdf35c69" - }, - "outputs": [], - "source": [ - "!nvcc --version" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "PkG3-jzTc5P3" - }, - "source": [ - "O comando e `nvidia-smi` _(Systems Management Interface)_ nos dá detalhes do ambiente, tais como aceleradores disponíveis e processos em execução:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 304, - "status": "ok", - "timestamp": 1667307719778, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "5HgjH3lddEaR", - "outputId": "1e6f3e0f-e7b5-462f-98fb-6d2f4ccce567" - }, - "outputs": [], - "source": [ - "!nvidia-smi" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "n7LU147xdQlI" - }, - "source": [ - "Para facilitar ainda mais, vamos instalar a extensão `nvcc_plugin` que permite escrever e executar código CUDA diretamente nas células do Jupyter:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 5127, - "status": "ok", - "timestamp": 1667307766872, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "DlKyxRLAdXKs", - "outputId": "43460365-d576-41a2-ac86-7a654008ad81" - }, - "outputs": [], - "source": [ - "!pip install git+https://github.com/andreinechaev/nvcc4jupyter.git\n", - "%load_ext nvcc_plugin" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ROGtfdUhdZty" - }, - "source": [ - "Depois disso, basta usar o prefixo `%%cu` no início da célula para rodar código CUDA diretamente no Jupyter sem a necessidade de invocar explicitamente o compilador:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 2719, - "status": "ok", - "timestamp": 1667307777506, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "8dISM5S7VcKI", - "outputId": "ca5b18b9-9c1e-49a5-ca6e-f701d56074cf" - }, - "outputs": [], - "source": [ - "%%cu\n", - "#include \n", - "int main() {\n", - " int nDevices;\n", - "\tstd::cout << \"Welcome to CUDA!\" << std::endl;\n", - " cudaGetDeviceCount(&nDevices);\n", - " for (int i = 0; i < nDevices; i++) {\n", - " cudaDeviceProp prop;\n", - " cudaGetDeviceProperties(&prop, i);\n", - " std::cout << \"Device Number: \" << i << std::endl;\n", - " std::cout << \" Device name: \" << prop.name << std::endl;\n", - " std::cout << \" Memory Clock Rate (KHz): \" << prop.memoryClockRate << std::endl;\n", - " std::cout << \" Memory Bus Width (bits): \" << prop.memoryBusWidth << std::endl;\n", - " std::cout << \" Peak Memory Bandwidth (GB/s): \" << 2.0*prop.memoryClockRate*(prop.memoryBusWidth/8)/1.0e6 << std::endl;\n", - " }\n", - "\treturn 0;\n", - "}\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "t2l60EtMeAhj" - }, - "source": [ - "---\n", - "## Pré-requisitos\n", - "\n", - "Para aproveitar ao máximo este laboratório, você já deve ser capaz de:\n", - "\n", - "- Declarar variáveis, escrever loops e usar instruções `if/else` em C.\n", - "- Definir e invocar funções em C.\n", - "- Alocar arrays em C.\n", - "\n", - "Nenhum conhecimento prévio de CUDA é necessário." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "HHO9Azzd5NjS" - }, - "source": [ - "---\n", - "## Objetivos\n", - "\n", - "Ao concluir este laboratório, você será capaz de:\n", - "\n", - "- Escrever, compilar e executar programas C/C++ que chamam funções de CPU e **disparam** **kernels** em GPU.\n", - "- Controlar a **hierarquia de threads** paralela usando a **configuração de execução**.\n", - "- Refatorar loops seriais para executar suas iterações em paralelo em uma GPU.\n", - "- Alocar e liberar memória disponível para CPUs e GPUs.\n", - "- Manipular erros gerados pelo código CUDA.\n", - "- Acelerar aplicativos somente de CPU." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "HEov_tLu6PJg" - }, - "source": [ - "---\n", - "## Escrevendo o código da aplicação para a GPU\n", - "\n", - "CUDA fornece extensões para muitas linguagens de programação comuns, no caso deste laboratório, C/C++. Essas extensões de linguagem permitem que os desenvolvedores executem facilmente funções em seu código-fonte em uma GPU.\n", - "\n", - "Abaixo está um arquivo `.cu` (extensão do arquivo para programas acelerados por CUDA). Ele contém duas funções, a primeira que será executada na CPU, a segunda que será executada na GPU. Gaste um tempo identificando as diferenças entre as funções, tanto em termos de como elas são definidas quanto de como são invocadas." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 291, - "status": "ok", - "timestamp": 1667307795961, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "Ond3PA7O7UYS", - "outputId": "8e254e99-2e3e-4792-b3f2-6555b026c67e" - }, - "outputs": [], - "source": [ - "%%writefile 01-hello-gpu.cu\n", - "#include \n", - "\n", - "void CPUFunction() {\n", - " printf(\"Esta função está definida para ser executada na CPU.\\n\");\n", - "}\n", - "\n", - "__global__ void GPUFunction() {\n", - " printf(\"Esta função está definida para ser executada na GPU.\\n\");\n", - "}\n", - "\n", - "int main() {\n", - " CPUFunction();\n", - " GPUFunction<<<1, 1>>>();\n", - " cudaDeviceSynchronize();\n", - " return 0;\n", - "}\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "WR3KDWbl7aUk" - }, - "source": [ - "Aqui estão algumas linhas de código importantes para destacar, bem como alguns outros termos comuns usados ​​em computação acelerada:\n", - "\n", - "`__global__ void GPUFunction()`\n", - " - A palavra-chave `__global__` indica que a seguinte função será executada na GPU e pode ser invocada **globalmente**, o que neste contexto significa tanto pela CPU quanto pela GPU.\n", - " - Muitas vezes, o código executado na CPU é chamado de código do **host**, e o código executado na GPU é chamado de código do **dispositivo** ou **acelerador**.\n", - " - Observe o tipo de retorno `void`. É necessário que as funções definidas com a palavra-chave `__global__` retornem o tipo `void`.\n", - "\n", - "`GPUFunction<<<1, 1>>>();`\n", - " - Normalmente, ao chamar uma função para execução na GPU, chamamos essa função de **kernel**, que é **disparada** ou **lançada**.\n", - " - Ao lançar um kernel, devemos fornecer uma **configuração de execução**, que é feita usando a sintaxe `<<< ... >>>` antes de passar ao kernel quaisquer argumentos esperados.\n", - " - Em um nível alto, a configuração de execução permite que os programadores especifiquem a **hierarquia de threads** para uma inicialização do kernel, que define o número de agrupamentos de threads (chamados **blocos**), bem como quantos **threads** para executar em cada bloco. A configuração de execução será explorada detalhadamente mais adiante no laboratório, mas, por enquanto, observe que o kernel está sendo iniciado com `1` bloco de threads (o primeiro argumento de configuração de execução) que contém `1` thread (o segundo argumento de configuração) .\n", - "\n", - "`cudaDeviceSynchronize();`\n", - " - Ao contrário de muitos códigos C/C++, o lançamento de kernels é **assíncrono**: o código da CPU continuará a ser executado *sem esperar que o lançamento do kernel seja concluído*.\n", - " - Uma chamada para `cudaDeviceSynchronize`, uma função fornecida pelo runtime CUDA, fará com que o código do host (CPU) espere até que o código do dispositivo (GPU) seja concluído e só então retome a execução na CPU." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "HI5h7LvV-XVX" - }, - "source": [ - "---\n", - "## Experimente você!\n", - "\n", - "Abaixo invocamos o compilador `nvcc` para compilar e executar (usando o parâmetro `-run`) o arquivo criado na célula anterior. Execute a célula abaixo para ver o resultado... \n", - "\n", - "O parâmetro `-arch=` permite escolher a arquitetura alvo da GPU, experimente trocar para `sm_50` e veja o que acontece. \n", - "\n", - "Volte na célula que gerou o arquivo e experimente mudar o número de blocos e threads. Não esqueça de executar a célula para salvar novamente o arquivo." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 1452, - "status": "ok", - "timestamp": 1667307811731, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "IJ5C8pxgWa_5", - "outputId": "15b6c10f-13b1-4882-e8ff-280c17486076" - }, - "outputs": [], - "source": [ - "!nvcc -arch=sm_70 -o hello-gpu 01-hello-gpu.cu -run" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "TvXwN0OMBG-7" - }, - "source": [ - "---\n", - "## Índices de threads e blocos\n", - "\n", - "Cada thread recebe um índice dentro de seu bloco de threads, começando em `0`. Além disso, cada bloco recebe um índice, começando em '0'. Assim como os encadeamentos são agrupados em blocos de encadeamentos, os blocos são agrupados em uma **grade (grid)**, que é a entidade mais alta na hierarquia de encadeamentos CUDA. Em resumo, os kernels CUDA são executados em uma grade de 1 ou mais blocos, com cada bloco contendo o mesmo número de 1 ou mais threads.\n", - "\n", - "Kernels CUDA têm acesso a variáveis ​​especiais que identificam tanto o índice da thread (dentro do bloco) que está executando o kernel, quanto o índice do bloco (dentro da grade) em que a thread está. Essas variáveis ​​são `threadIdx.x` e `blockIdx.x` respectivamente." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 260, - "status": "ok", - "timestamp": 1667307842019, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "9UQu-f1xBVrf", - "outputId": "629f88c5-3909-4123-e4d6-46bf38ab4d4c" - }, - "outputs": [], - "source": [ - "%%writefile 02-thread-and-block-idx.cu\n", - "#include \n", - "\n", - "__global__ void printSuccessForCorrectExecutionConfiguration() {\n", - " if(threadIdx.x == 1023 && blockIdx.x == 255)\n", - " printf(\"Success!\\n\");\n", - "}\n", - "\n", - "int main() {\n", - " /* This is one possible execution context that will make\n", - " * the kernel launch print its success message. */\n", - " printSuccessForCorrectExecutionConfiguration<<<256, 1024>>>();\n", - "\n", - " /* Don't forget kernel execution is asynchronous and you must\n", - " * sync on its completion. */\n", - " cudaDeviceSynchronize();\n", - "}\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 1877, - "status": "ok", - "timestamp": 1667307846101, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "_niddzloB3VK", - "outputId": "29172228-e427-4ebb-93fa-e6049a433d58" - }, - "outputs": [], - "source": [ - "!nvcc -arch=sm_70 -o thread-and-block-idx 02-thread-and-block-idx.cu -run" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "P4T9a5AFDU6j" - }, - "source": [ - "---\n", - "## Acelerando loops `for` \n", - "\n", - "Loops `for` em aplicativos somente de CPU estão prontos para aceleração: em vez de executar cada iteração do loop em série, cada iteração do loop pode ser executada em paralelo em seu próprio thread. Considere o seguinte loop for e observe, embora seja óbvio, que ele controla quantas vezes o loop será executado, além de definir o que acontecerá para cada iteração do loop:\n", - "\n", - "```cpp\n", - "int N = 2<<20;\n", - "for (int i = 0; i < N; ++i) {\n", - " printf(\"%d\\n\", i);\n", - "}\n", - "```\n", - "\n", - "Para paralelizar este loop, 2 passos devem ser seguidos:\n", - "\n", - "- Um kernel deve ser escrito para fazer o trabalho de uma **única iteração do loop**.\n", - "- Como o kernel será independente de outros kernels em execução, a configuração de execução deve ser tal que o kernel execute o número correto de vezes, por exemplo, o número de vezes que o loop teria iterado." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 291, - "status": "ok", - "timestamp": 1667308778082, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "Kd67suEUV2jq", - "outputId": "326b74be-c8b1-47e4-834d-8300114e976a" - }, - "outputs": [], - "source": [ - "%%writefile 03-single-block-loop.cu\n", - "#include \n", - "\n", - "/*\n", - " * Notice the absence of the previously expected argument `N`.\n", - " */\n", - "\n", - "__global__ void loop() {\n", - " /* This kernel does the work of only 1 iteration\n", - " * of the original for loop. Indication of which\n", - " * \"iteration\" is being executed by this kernel is\n", - " * still available via `threadIdx.x`. */\n", - " printf(\"This is iteration number %d\\n\", threadIdx.x);\n", - "}\n", - "\n", - "int main() {\n", - " /* It is the execution context that sets how many \"iterations\"\n", - " * of the \"loop\" will be done.\n", - " */\n", - " loop<<<1, 10>>>();\n", - " cudaDeviceSynchronize();\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "executionInfo": { - "elapsed": 2503, - "status": "ok", - "timestamp": 1667308785192, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "ve8rYwyOWIhx" - }, - "outputs": [], - "source": [ - "!nvcc -arch=sm_70 -o single-block-loop 03-single-block-loop.cu -run" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "760U_zyFWt-j" - }, - "source": [ - "---\n", - "## Usando dimensões de bloco para mais paralelização\n", - "\n", - "Há um limite para o número de threads que podem existir em um bloco de thread: 1024 para ser preciso. Para aumentar a quantidade de paralelismo em aplicativos acelerados, devemos ser capazes de coordenar entre vários blocos de threads.\n", - "\n", - "Kernels CUDA têm acesso a uma variável especial que fornece o número de threads em um bloco: `blockDim.x`. Usando essa variável, em conjunto com `blockIdx.x` e `threadIdx.x`, a paralelização aumentada pode ser realizada organizando a execução paralela em vários blocos de vários threads com a expressão idiomática `threadIdx.x + blockIdx.x * blockDim.x `. Aqui está um exemplo detalhado.\n", - "\n", - "A configuração de execução `<<<10, 10>>>` lançaria um grid com um total de 100 threads, contidos em 10 blocos de 10 threads. Esperamos, portanto, que cada thread tenha a capacidade de calcular algum índice exclusivo entre `0` e `99`.\n", - "\n", - "- Se o bloco `blockIdx.x` for igual a `0`, então `blockIdx.x * blockDim.x` será `0`. Adicionando a `0` os possíveis valores `threadIdx.x` `0` a `9`, então podemos gerar os índices `0` a `9` dentro da grade de 100 threads.\n", - "- Se o bloco `blockIdx.x` for igual a `1`, então `blockIdx.x * blockDim.x` será `10`. Adicionando a `10` os possíveis valores `threadIdx.x` `0` a `9`, então podemos gerar os índices `10` a `19` dentro da grade de 100 threads.\n", - "- Se o bloco `blockIdx.x` for igual a `5`, então `blockIdx.x * blockDim.x` será `50`. Adicionando a `50` os possíveis valores `threadIdx.x` de `0` a `9`, podemos gerar os índices de `50` a `59` dentro da grade de 100 threads.\n", - "- Se o bloco `blockIdx.x` for igual a `9`, então `blockIdx.x * blockDim.x` será `90`. Adicionando a `90` os possíveis valores `threadIdx.x` `0` a `9`, então podemos gerar os índices `90` a `99` dentro da grade de 100 threads." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 353, - "status": "ok", - "timestamp": 1667309141685, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "SuWsdxjQXXu5", - "outputId": "2dc6f1a1-b9a1-40f4-abd0-d9d3d7ea796d" - }, - "outputs": [], - "source": [ - "%%writefile 04-multi-block-loop.cu\n", - "#include \n", - "\n", - "__global__ void loop() {\n", - " /* This idiomatic expression gives each thread\n", - " * a unique index within the entire grid.\n", - " */\n", - " int i = blockIdx.x * blockDim.x + threadIdx.x;\n", - " printf(\"%d\\n\", i);\n", - "}\n", - "\n", - "int main() {\n", - " /* Additional execution configurations that would\n", - " * work and meet the exercises contraints are:\n", - " * <<<5, 2>>>\n", - " * <<<10, 1>>> */\n", - " loop<<<2, 5>>>();\n", - " cudaDeviceSynchronize();\n", - "}\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "executionInfo": { - "elapsed": 1383, - "status": "ok", - "timestamp": 1667309158968, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "mox8jBd9Xlka" - }, - "outputs": [], - "source": [ - "!nvcc -arch=sm_70 -o multi-block-loop 04-multi-block-loop.cu -run" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "PiY6WGLKX97C" - }, - "source": [ - "---\n", - "## Alocação de memória a ser acessada na GPU e na CPU\n", - "\n", - "Versões mais recentes do CUDA (versão 6 e posterior) facilitaram a alocação de memória disponível tanto para o host da CPU quanto para qualquer número de dispositivos GPU e, embora existam muitas [técnicas intermediárias e avançadas](http://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html#memory-optimizations) para gerenciamento de memória que oferecerá suporte ao melhor desempenho em aplicativos acelerados, a técnica de gerenciamento de memória CUDA mais básica que abordaremos agora oferece suporte a ganhos de desempenho fantásticos em aplicativos somente de CPU com quase nenhuma sobrecarga de desenvolvedor.\n", - "\n", - "Para alocar e liberar memória e obter um ponteiro que possa ser referenciado no código do host e do dispositivo, substitua as chamadas para `malloc` e `free` por `cudaMallocManaged` e `cudaFree` como no exemplo a seguir:\n", - "\n", - "\n", - "```cpp\n", - "// CPU-only\n", - "int N = 2<<20;\n", - "size_t size = N * sizeof(int);\n", - "\n", - "int *a;\n", - "a = (int *)malloc(size);\n", - "\n", - "// Use `a` in CPU-only program.\n", - "free(a);\n", - "```\n", - "\n", - "```cpp\n", - "// Accelerated\n", - "int N = 2<<20;\n", - "size_t size = N * sizeof(int);\n", - "\n", - "int *a;\n", - "// Note the address of `a` is passed as first argument.\n", - "cudaMallocManaged(&a, size);\n", - "\n", - "// Use `a` on the CPU and/or on any GPU in the accelerated system.\n", - "cudaFree(a);\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 8, - "status": "ok", - "timestamp": 1667309592578, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "vLqWyytXYlyH", - "outputId": "4fa0a076-efc5-422c-b483-76040663c04c" - }, - "outputs": [], - "source": [ - "%%writefile 05-double-elements.cu\n", - "#include \n", - "\n", - "void init(int *a, int N) {\n", - " int i;\n", - " for (i = 0; i < N; ++i) \n", - " a[i] = i;\n", - "}\n", - "\n", - "__global__ void doubleElements(int *a, int N) {\n", - " int i;\n", - " i = blockIdx.x * blockDim.x + threadIdx.x;\n", - " if (i < N)\n", - " a[i] *= 2;\n", - "}\n", - "\n", - "bool checkElementsAreDoubled(int *a, int N) {\n", - " int i;\n", - " for (i = 0; i < N; ++i)\n", - " if (a[i] != i*2) \n", - " return false;\n", - " return true;\n", - "}\n", - "\n", - "int main() {\n", - " int N = 1000;\n", - " int *a;\n", - "\n", - " size_t size = N * sizeof(int);\n", - " /* Use `cudaMallocManaged` to allocate pointer `a` available\n", - " * on both the host and the device. */\n", - "\n", - " cudaMallocManaged(&a, size);\n", - " init(a, N);\n", - "\n", - " size_t threads_per_block = 256;\n", - " size_t number_of_blocks = (N + threads_per_block - 1) / threads_per_block;\n", - "\n", - " doubleElements<<>>(a, N);\n", - " cudaDeviceSynchronize();\n", - "\n", - " bool areDoubled = checkElementsAreDoubled(a, N);\n", - " printf(\"All elements were doubled? %s\\n\", areDoubled ? \"TRUE\" : \"FALSE\");\n", - "\n", - " /* Use `cudaFree` to free memory allocated with `cudaMallocManaged`. */\n", - " cudaFree(a);\n", - "}\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 2682, - "status": "ok", - "timestamp": 1667309600259, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "1I9iWhbUZEN1", - "outputId": "d897d113-50b8-4a76-87c7-fe8071d5a6ae" - }, - "outputs": [], - "source": [ - "!nvcc -arch=sm_70 -o double-elements 05-double-elements.cu -run" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "4_hRvjuIZ-tP" - }, - "source": [ - "---\n", - "## Tratamento de incompatibilidades de configuração de bloco com o número de threads necessários\n", - "\n", - "Pode ser que uma configuração de execução não possa ser expressa para criar o número exato de threads necessários para paralelizar um loop.\n", - "\n", - "Um exemplo comum tem a ver com o desejo de escolher tamanhos de bloco ideais. Por exemplo, devido às características de hardware da GPU, os blocos que contêm vários threads que são múltiplos de 32 geralmente são desejáveis ​​para benefícios de desempenho. Supondo que queríamos lançar blocos cada um contendo 256 threads (um múltiplo de 32) e precisávamos executar 1.000 tarefas paralelas (um número trivialmente pequeno para facilitar a explicação), então não há número de blocos que produziria um total exato de 1000 threads na grade, pois não há valor inteiro 32 pode ser multiplicado por exatamente 1000.\n", - "\n", - "Este cenário pode ser facilmente resolvido da seguinte maneira:\n", - "\n", - "- Escreva uma configuração de execução que crie **mais** threads do que o necessário para realizar o trabalho alocado.\n", - "- Passar um valor como argumento no kernel (`N`) que representa o tamanho total do conjunto de dados a ser processado, ou o total de threads que são necessários para concluir o trabalho.\n", - "- Após calcular o índice da thread dentro da grade (usando `tid+bid*bdim`), verifique se este índice não excede `N`, e só execute o trabalho pertinente do kernel se não exceder.\n", - "\n", - "Aqui está um exemplo de uma maneira idiomática de escrever uma configuração de execução quando tanto `N` quanto o número de threads em um bloco são conhecidos e uma correspondência exata entre o número de threads na grade e `N` não pode ser garantida. Ele garante que sempre haja pelo menos tantos encadeamentos quantos forem necessários para `N`, e apenas 1 bloco adicional de encadeamentos extras, no máximo:\n", - "\n", - "```cpp\n", - "// Suponha que `N` seja conhecido\n", - "int N = 100000;\n", - "\n", - "// Suponha que desejamos definir `threads_per_block` exatamente como `256`\n", - "size_t threads_per_block = 256;\n", - "\n", - "// Certifique-se de que haja pelo menos `N` threads na grade, com apenas 1 bloco excedente\n", - "size_t number_of_blocks = (N + threads_per_block - 1) / threads_per_block;\n", - "\n", - "some_kernel<<>>(N);\n", - "```\n", - "\n", - "Como a configuração de execução acima resulta em mais threads na grade do que `N`, deve-se tomar cuidado dentro da definição de `some_kernel` para que `some_kernel` não tente acessar elementos de dados fora do intervalo, ao ser executado por um dos tópicos \"extras\":\n", - "\n", - "```cpp \n", - "__global__ some_kernel(int N) {\n", - " int idx = threadIdx.x + blockIdx.x * blockDim.x;\n", - " if (idx < N) { // Verifica se `idx` mapeia para algum valor dentro de `N`\n", - " // Só executa em caso verdadeiro\n", - " }\n", - "}\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 258, - "status": "ok", - "timestamp": 1667310438966, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "cb5iL-65cCDN", - "outputId": "d0975eae-30b1-4458-b6e3-6188823fe413" - }, - "outputs": [], - "source": [ - "%%writefile 06-mismatched-config-loop.cu\n", - "#include \n", - "\n", - "__global__ void initializeElementsTo(int initialValue, int *a, int N) {\n", - " int i = threadIdx.x + blockIdx.x * blockDim.x;\n", - " if (i < N) \n", - " a[i] = initialValue;\n", - "}\n", - "\n", - "int main() {\n", - " /* Do not modify `N`. */\n", - " int N = 1000;\n", - "\n", - " int *a;\n", - " size_t size = N * sizeof(int);\n", - "\n", - " cudaMallocManaged(&a, size);\n", - "\n", - " /* Assume we have reason to want the number of threads\n", - " * fixed at `256`: do not modify `threads_per_block`. */\n", - " size_t threads_per_block = 256;\n", - "\n", - " /* The following is idiomatic CUDA to make sure there are at\n", - " * least as many threads in the grid as there are `N` elements. */\n", - " size_t number_of_blocks = (N + threads_per_block - 1) / threads_per_block;\n", - "\n", - " int initialValue = 6;\n", - "\n", - " initializeElementsTo<<>>(initialValue, a, N);\n", - " cudaDeviceSynchronize();\n", - "\n", - " /* Check to make sure all values in `a`, were initialized. */\n", - " for (int i = 0; i < N; ++i) \n", - " if(a[i] != initialValue) {\n", - " printf(\"FAILURE: target value: %d\\t a[%d]: %d\\n\", initialValue, i, a[i]);\n", - " cudaFree(a);\n", - " exit(1);\n", - " }\n", - " printf(\"SUCCESS!\\n\");\n", - " cudaFree(a);\n", - "}\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "gWq-PRRyclAg" - }, - "outputs": [], - "source": [ - "!nvcc -arch=sm_70 -o mismatched-config-loop 06-mismatched-config-loop.cu -run" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Cz1hUbv3dJuH" - }, - "source": [ - "---\n", - "## Conjuntos de dados maiores que a grade\n", - "\n", - "Por opção, geralmente para criar a configuração de execução com melhor desempenho, ou por necessidade, o número de threads em uma grade pode ser menor que o tamanho de um conjunto de dados. Considere uma matriz com 1000 elementos e uma grade com 250 threads (usando tamanhos triviais aqui para facilitar a explicação). Aqui, cada thread na grade precisará ser usado 4 vezes. Um método comum para fazer isso é usar um **grid-stride loop** dentro do kernel.\n", - "\n", - "Em um loop grid-stride, cada thread calculará seu índice exclusivo dentro da grade usando `tid+bid*bdim`, realizará sua operação no elemento naquele índice dentro da matriz e, em seguida, adicionará ao seu índice o número de threads na grade e repete isso, até que esteja fora do alcance da matriz. Por exemplo, para uma matriz de 500 elementos e uma grade de 250 threads, a thread com índice 20 na grade seria:\n", - "\n", - "- Realize sua operação no elemento 20 do array de 500 elementos\n", - "- Incrementar seu índice em 250, o tamanho da grade, resultando em 270\n", - "- Realize sua operação no elemento 270 do array de 500 elementos\n", - "- Incrementar seu índice em 250, o tamanho da grade, resultando em 520\n", - "- Como 520 agora está fora do alcance do array, o encadeamento interromperá seu trabalho\n", - "\n", - "CUDA fornece uma variável especial que fornece o número de blocos em uma grade, `gridDim.x`. Calcular o número total de threads em uma grade é simplesmente o número de blocos em uma grade multiplicado pelo número de threads em cada bloco, `gridDim.x * blockDim.x`. Com isso em mente, aqui está um exemplo detalhado de um loop grid-stride dentro de um kernel:\n", - "\n", - "\n", - "```cpp\n", - "__global__ void kernel(int *a, int N) {\n", - " int indexWithinTheGrid = threadIdx.x + blockIdx.x * blockDim.x;\n", - " int gridStride = gridDim.x * blockDim.x;\n", - " for (int i = indexWithinTheGrid; i < N; i += gridStride) {\n", - " // do work on a[i];\n", - " }\n", - "}\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 261, - "status": "ok", - "timestamp": 1667310825043, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "4iMHr2Kvdv8C", - "outputId": "1953aff7-b207-4c35-db47-9f2edf01c490" - }, - "outputs": [], - "source": [ - "%%writefile 07-grid-stride-double.cu\n", - "#include \n", - "\n", - "void init(int *a, int N) {\n", - " int i;\n", - " for (i = 0; i < N; ++i) \n", - " a[i] = i;\n", - "}\n", - "\n", - "__global__ void doubleElements(int *a, int N) {\n", - " /* Use a grid-stride loop so each thread does work\n", - " * on more than one element in the array. */\n", - " int idx = blockIdx.x * blockDim.x + threadIdx.x;\n", - " int stride = gridDim.x * blockDim.x;\n", - "\n", - " for (int i = idx; i < N; i += stride) \n", - " a[i] *= 2;\n", - "}\n", - "\n", - "bool checkElementsAreDoubled(int *a, int N) {\n", - " int i;\n", - " for (i = 0; i < N; ++i) \n", - " if (a[i] != i*2) \n", - " return false;\n", - " return true;\n", - "}\n", - "\n", - "int main() {\n", - " int N = 10000;\n", - " int *a;\n", - "\n", - " size_t size = N * sizeof(int);\n", - " cudaMallocManaged(&a, size);\n", - "\n", - " init(a, N);\n", - "\n", - " size_t threads_per_block = 256;\n", - " size_t number_of_blocks = 32;\n", - "\n", - " doubleElements<<>>(a, N);\n", - " cudaDeviceSynchronize();\n", - "\n", - " bool areDoubled = checkElementsAreDoubled(a, N);\n", - " printf(\"All elements were doubled? %s\\n\", areDoubled ? \"TRUE\" : \"FALSE\");\n", - "\n", - " cudaFree(a);\n", - "}\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 2476, - "status": "ok", - "timestamp": 1667310854061, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "E54xcvLxeDoZ", - "outputId": "d684c772-c4d2-432a-c6c4-fade4b161f30" - }, - "outputs": [], - "source": [ - "!nvcc -arch=sm_70 -o grid-stride-double 07-grid-stride-double.cu -run" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "3gN2npqAetcR" - }, - "source": [ - "---\n", - "## Tratamento de erros\n", - "\n", - "Como em qualquer aplicativo, o tratamento de erros no código CUDA acelerado é essencial. Muitas, se não a maioria das funções CUDA (veja, por exemplo, as [funções de gerenciamento de memória](http://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__MEMORY.html#group__CUDART__MEMORY)) retornam um valor do tipo `cudaError_t`, que pode ser usado para verificar se ocorreu ou não um erro ao chamar a função. Aqui está um exemplo onde o tratamento de erros é executado para uma chamada para `cudaMallocManaged`:\n", - "\n", - "```cpp\n", - "cudaError_t err;\n", - "err = cudaMallocManaged(&a, N) // Assumindo a existência de `a` e `N`.\n", - "\n", - "if (err != cudaSuccess) { // `cudaSuccess` é fornecido pelo CUDA.\n", - " printf(\"Erro: %s\\n\", cudaGetErrorString(err)); // `cudaGetErrorString` é fornecido pelo CUDA.\n", - "}\n", - "```\n", - "\n", - "O lançamento de kernels, que são definidos para retornar `void`, não retorna um valor do tipo `cudaError_t`. Para verificar os erros que ocorrem no momento da inicialização do kernel, por exemplo, se a configuração de inicialização estiver incorreta, o CUDA fornece a função `cudaGetLastError`, que retorna um valor do tipo `cudaError_t`.\n", - "\n", - "```cpp\n", - "/* Este lançamento deve causar um erro, mas o próprio kernel não pode devolver. */\n", - "algumKernel<<<1, -1>>>(); // -1 não é um número válido de threads.\n", - "\n", - "cudaError_t err;\n", - "err = cudaGetLastError(); // `cudaGetLastError` retornará o erro acima.\n", - "if (err!= cudaSucesso) {\n", - " printf(\"Erro: %s\\n\", cudaGetErrorString(err));\n", - "}\n", - "```\n", - "\n", - "Finalmente, para capturar erros que ocorrem de forma assíncrona, por exemplo, durante a execução de um kernel assíncrono, é essencial verificar o status retornado por uma chamada de API de tempo de execução CUDA de sincronização subsequente, como `cudaDeviceSynchronize`, que retornará um erro se um dos kernels lançados anteriormente falhar." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 355, - "status": "ok", - "timestamp": 1667311379791, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "v6a6qaxOfrZP", - "outputId": "dc54fd6b-14a1-473c-a9c2-d7083de99080" - }, - "outputs": [], - "source": [ - "%%writefile 08-add-error-handling.cu\n", - "#include \n", - "\n", - "void init(int *a, int N) {\n", - " int i;\n", - " for (i = 0; i < N; ++i)\n", - " a[i] = i;\n", - "}\n", - "\n", - "__global__ void doubleElements(int *a, int N) {\n", - " int idx = blockIdx.x * blockDim.x + threadIdx.x;\n", - " int stride = gridDim.x * blockDim.x;\n", - "\n", - " /* The previous code (now commented out) attempted\n", - " * to access an element outside the range of `a`. */\n", - "\n", - " // for (int i = idx; i < N + stride; i += stride)\n", - " for (int i = idx; i < N; i += stride)\n", - " a[i] *= 2;\n", - "}\n", - "\n", - "bool checkElementsAreDoubled(int *a, int N) {\n", - " int i;\n", - " for (i = 0; i < N; ++i)\n", - " if (a[i] != i*2) \n", - " return false;\n", - " return true;\n", - "}\n", - "\n", - "int main() {\n", - " int N = 10000;\n", - " int *a;\n", - "\n", - " size_t size = N * sizeof(int);\n", - " cudaMallocManaged(&a, size);\n", - "\n", - " init(a, N);\n", - "\n", - " /* The previous code (now commented out) attempted to launch\n", - " * the kernel with more than the maximum number of threads per\n", - " * block, which is 1024. */\n", - " size_t threads_per_block = 1024;\n", - " /* size_t threads_per_block = 2048; */\n", - " size_t number_of_blocks = 32;\n", - "\n", - " cudaError_t syncErr, asyncErr;\n", - "\n", - " doubleElements<<>>(a, N);\n", - "\n", - " /* Catch errors for both the kernel launch above and any\n", - " * errors that occur during the asynchronous `doubleElements`\n", - " * kernel execution. */\n", - "\n", - " syncErr = cudaGetLastError();\n", - " asyncErr = cudaDeviceSynchronize();\n", - "\n", - " /* Print errors should they exist. */\n", - "\n", - " if (syncErr != cudaSuccess) printf(\"Error: %s\\n\", cudaGetErrorString(syncErr));\n", - " if (asyncErr != cudaSuccess) printf(\"Error: %s\\n\", cudaGetErrorString(asyncErr));\n", - "\n", - " bool areDoubled = checkElementsAreDoubled(a, N);\n", - " printf(\"All elements were doubled? %s\\n\", areDoubled ? \"TRUE\" : \"FALSE\");\n", - "\n", - " cudaFree(a);\n", - "}\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 1728, - "status": "ok", - "timestamp": 1667311416535, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "xWsKz0tigKkF", - "outputId": "29b8ee2d-5e27-47bb-e281-a7951f409452" - }, - "outputs": [], - "source": [ - "!nvcc -arch=sm_70 -o add-error-handling 08-add-error-handling.cu -run" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "kItQSgU4gYls" - }, - "source": [ - "---\n", - "## Resumo\n", - "\n", - "Neste momento, você atingiu todos os seguintes objetivos de laboratório:\n", - "\n", - "- Escrever, compilar e executar programas C/C++ que chamam funções de CPU e **lançament\n", - "o** de **kernels** de GPU.\n", - "- Controlar a **hierarquia de threads** paralela usando a **configuração de execução**.\n", - "- Refatorar loops seriais para executar suas iterações em paralelo em uma GPU.\n", - "- Alocar e liberar memória disponível para CPUs e GPUs.\n", - "- Manipular erros gerados pelo código CUDA.\n", - "\n", - "Agora você concluirá o objetivo final do laboratório:\n", - "\n", - "- Acelerar aplicativos somente de CPU." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "-N1D207PlMym" - }, - "source": [ - "***\n", - "## Sua vez! \n", - "\n", - "O código serial a seguir realiza a soma de dois vetores. Tente modificar o código com o que você aprendeu até agora para torná-lo paralelo. \n", - "\n", - "Para facilitar, eu coloquei na célula seguinte as mudanças necessárias. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 258, - "status": "ok", - "timestamp": 1667312847290, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "7K3bRuQmlizD", - "outputId": "9396a87e-db38-4352-f91b-324dc6fe24f0" - }, - "outputs": [], - "source": [ - "%%writefile 09-vector-add.cu\n", - "#include \n", - "\n", - "void initWith(float num, float *a, int N)\n", - "{\n", - " for(int i = 0; i < N; ++i)\n", - " {\n", - " a[i] = num;\n", - " }\n", - "}\n", - "\n", - "void addVectorsInto(float *result, float *a, float *b, int N)\n", - "{\n", - " for(int i = 0; i < N; ++i)\n", - " {\n", - " result[i] = a[i] + b[i];\n", - " }\n", - "}\n", - "\n", - "void checkElementsAre(float target, float *array, int N)\n", - "{\n", - " for(int i = 0; i < N; i++)\n", - " {\n", - " if(array[i] != target)\n", - " {\n", - " printf(\"FAIL: array[%d] - %0.0f does not equal %0.0f\\n\", i, array[i], target);\n", - " exit(1);\n", - " }\n", - " }\n", - " printf(\"SUCCESS! All values added correctly.\\n\");\n", - "}\n", - "\n", - "int main()\n", - "{\n", - " const int N = 2<<20;\n", - " size_t size = N * sizeof(float);\n", - "\n", - " float *a;\n", - " float *b;\n", - " float *c;\n", - "\n", - " a = (float *)malloc(size);\n", - " b = (float *)malloc(size);\n", - " c = (float *)malloc(size);\n", - "\n", - " initWith(3, a, N);\n", - " initWith(4, b, N);\n", - " initWith(0, c, N);\n", - "\n", - " addVectorsInto(c, a, b, N);\n", - "\n", - " checkElementsAre(7, c, N);\n", - "\n", - " free(a);\n", - " free(b);\n", - " free(c);\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!nvcc -arch=sm_70 -o vector-add 09-vector-add.cu -run" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "DYkmlJPqieUf" - }, - "source": [ - "```diff\n", - "1a2,11\n", - "+ #include \n", - "+ \n", - "+ inline cudaError_t checkCuda(cudaError_t result)\n", - "+ {\n", - "+ if (result != cudaSuccess) {\n", - "+ fprintf(stderr, \"CUDA Runtime Error: %s\\n\", cudaGetErrorString(result));\n", - "+ assert(result == cudaSuccess);\n", - "+ }\n", - "+ return result;\n", - "+ }\n", - "10a21\n", - "+ __global__\n", - "13c24,27\n", - "- for(int i = 0; i - N; ++i)\n", - "---\n", - "+ int index = threadIdx.x + blockIdx.x * blockDim.x;\n", - "+ int stride = blockDim.x * gridDim.x;\n", - "+ \n", - "+ for(int i = index; i - N; i += stride)\n", - "41,43c55,57\n", - "- a = (float *)malloc(size);\n", - "- b = (float *)malloc(size);\n", - "- c = (float *)malloc(size);\n", - "---\n", - "+ checkCuda( cudaMallocManaged(&a, size) );\n", - "+ checkCuda( cudaMallocManaged(&b, size) );\n", - "+ checkCuda( cudaMallocManaged(&c, size) );\n", - "49c63,72\n", - "- addVectorsInto(c, a, b, N);\n", - "---\n", - "+ size_t threadsPerBlock;\n", - "+ size_t numberOfBlocks;\n", - "+ \n", - "+ threadsPerBlock = 256;\n", - "+ numberOfBlocks = (N + threadsPerBlock - 1) / threadsPerBlock;\n", - "+ \n", - "+ addVectorsInto<<>>(c, a, b, N);\n", - "+ \n", - "+ checkCuda( cudaGetLastError() );\n", - "+ checkCuda( cudaDeviceSynchronize() );\n", - "53,55c76,78\n", - "- free(a);\n", - "- free(b);\n", - "- free(c);\n", - "---\n", - "+ checkCuda( cudaFree(a) );\n", - "+ checkCuda( cudaFree(b) );\n", - "+ checkCuda( cudaFree(c) );\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "WnHPXbHmf4Pn" - }, - "source": [ - "___\n", - "# CUDA C/C++ vs. Numba vs. pyCUDA\n", - "\n", - "A maneira mais comum de programar em CUDA é com as extensões de linguagem CUDA C/C++. Com relação ao Python, o pyCUDA é, além do Numba, uma alternativa para acelerar o código Python em GPUs. Vale a pena fazer uma rápida comparação das três opções mencionadas antes de começarmos:\n", - "\n", - "### CUDA C/C++:\n", - "\n", - "- A maneira mais comum, eficiente e flexível de utilizar CUDA\n", - "- Acelera aplicações C/C++\n", - "\n", - "### pyCUDA:\n", - "\n", - "- Expõe toda a API CUDA C/C++\n", - "- É a opção CUDA com melhor desempenho disponível para Python\n", - "- Requer escrever código C em seu Python e, em geral, muitas modificações de código\n", - "\n", - "### Numba:\n", - "\n", - "- Potencialmente menos desempenho que pyCUDA\n", - "- Não expõe (ainda?) toda a API CUDA C/C++\n", - "- Ainda permite aceleração massiva, geralmente com muito pouca modificação de código\n", - "- Permite aos desenvolvedores a conveniência de escrever código diretamente em Python\n", - "- Também otimiza o código Python para a CPU" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Primeiros passos: Compilação para CPU\n", - "Se você se lembra, o Numba pode ser usado para otimizar o código para uma CPU ou GPU. Como introdução, e antes de passar para a aceleração de GPU, vamos escrever nossa primeira função Numba e compilá-la para o **CPU**. Ao fazer isso, teremos uma entrada fácil na sintaxe do Numba e também teremos a oportunidade um pouco mais tarde de comparar o desempenho do código Numba otimizado por CPU com o código Numba acelerado por GPU.\n", - "\n", - "O compilador Numba normalmente é ativado aplicando um [**function decorator**](https://en.wikipedia.org/wiki/Python_syntax_and_semantics#Decorators) a uma função Python. Decoradores são modificadores de função que transformam as funções Python que eles decoram, usando uma sintaxe muito simples. Aqui usaremos o decorador de compilação da CPU do Numba `@jit`:\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 2006, - "status": "ok", - "timestamp": 1667312825454, - "user": { - "displayName": "Ricardo Menotti - UFSCar", - "userId": "06481705935960912252" - }, - "user_tz": 180 - }, - "id": "JKFNQNhWglH7", - "outputId": "55c680c6-e656-4e22-e9e6-6e2b29581ec6" - }, - "outputs": [], - "source": [ - "from numba import jit\n", - "import math\n", - "\n", - "# This is the function decorator syntax and is equivalent to `hypot = jit(hypot)`.\n", - "# The Numba compiler is just a function you can call whenever you want!\n", - "@jit\n", - "def hypot(x, y):\n", - " # Implementation from https://en.wikipedia.org/wiki/Hypot\n", - " x = abs(x);\n", - " y = abs(y);\n", - " t = min(x, y);\n", - " x = max(x, y);\n", - " t = t / x;\n", - " return x * math.sqrt(1+t*t)\n", - "\n", - "hypot(3.0, 4.0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Entraremos em mais detalhes abaixo sobre o que acontece quando `hypot` é chamado, mas por enquanto saiba que na primeira vez que chamamos `hypot`, o compilador é acionado e compila uma implementação de código de máquina da função para entradas float. O Numba também salva a implementação original do Python da função no atributo `.py_func`, para que possamos chamar o código original do Python para ter certeza de obter a mesma resposta:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "hypot.py_func(3.0, 4.0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### BenchMark\n", - "\n", - "Uma parte importante do uso do Numba é medir o desempenho do seu novo código. Vamos ver se realmente aceleramos alguma coisa. A maneira mais fácil de fazer isso em um notebook Jupyter, como aquele em que esta sessão é executada, é usar a [`%timeit` função mágica](https://ipython.readthedocs.io/en/stable/interactive/magics .html#magic-timeit). Vamos primeiro medir a velocidade do Python original:\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%timeit hypot(3.0, 4.0)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%timeit math.hypot(3.0, 4.0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "O built-in do Python é ainda mais rápido que o Numba! Isso ocorre porque o Numba introduz alguma sobrecarga para cada chamada de função que é maior que a sobrecarga de chamada de função do próprio Python. Funções extremamente rápidas (como a acima) serão prejudicadas por isso. (Como um aparte, se você chamar uma função Numba de outra, haverá muito pouca sobrecarga de função, às vezes até zero se o compilador inserir a função na outra. Em resumo, sempre faça benchmark de suas funções para obter evidências de aceleração.)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercício: Uso do Numba para compilação de uma função para CPU\n", - "\n", - "A função a seguir usa [o método de Monte Carlo para determinar o Pi](https://academo.org/demos/estimating-pi-monte-carlo/). \n", - "A função em si já está funcionando, então não se preocupe com os detalhes matemáticos da implementação.\n", - "\n", - "Complete as tasks para compilar `monte_carlo_pi` com Numba antes de executar as 3 células a seguir que irão:\n", - "\n", - " 1. Confirme se a versão compilada está se comportando da mesma forma que a versão não compilada.\n", - " 2. Faça um benchmark da versão não compilada.\n", - " 3. Faça um benchmark da versão compilada.\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# TODO: Import Numba's just-in-time compiler function\n", - "from numba import jit\n", - "import random\n", - "\n", - "# We will use numpy's `testing` library to confirm compiled and uncompiled versions run the same\n", - "from numpy import testing\n", - "\n", - "nsamples = 1000000\n", - "\n", - "\n", - "# TODO: Use the Numba compiler to compile this function\n", - "@jit\n", - "def monte_carlo_pi(nsamples):\n", - " acc = 0\n", - " for i in range(nsamples):\n", - " x = random.random()\n", - " y = random.random()\n", - " if (x**2 + y**2) < 1.0:\n", - " acc += 1\n", - " return 4.0 * acc / nsamples\n", - "\n", - "# This assertion will fail until you successfully complete the exercise one cell above\n", - "testing.assert_almost_equal(monte_carlo_pi(nsamples), monte_carlo_pi.py_func(nsamples), decimal=2)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%timeit monte_carlo_pi(nsamples)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%timeit monte_carlo_pi.py_func(nsamples)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Funcionamento do Numba\n", - "\n", - "Agora que tivemos o primeiro contato usando o compilador Numba, vamos dar um olhada no que realmente está ocorrendo por baixo dos panos. A primeira vez que chamamos nossa função `hypot` encapsulada em Numba, o seguinte processo foi iniciado:\n", - "\n", - "![Numba Flowchart](images/numba_flowchart.png \"The compilation process\")\n", - "\n", - "\n", - "Podemos ver o resultado da inferência de tipo usando o método `.inspect_types()`, que imprime uma versão anotada do código-fonte:\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "hypot.inspect_types()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Observe que os nomes de tipo de Numba tendem a se assemelhar aos [tipos de dados do Numpy](https://docs.scipy.org/doc/numpy-1.13.0/user/basics.types.html), então um Python `float` é um `float64` (também chamado de \"precisão dupla\" em outras linguagens). Observar os tipos de dados às vezes pode ser importante no código da GPU porque o desempenho dos cálculos `float32` e `float64` pode (dependendo da GPU) ser muito diferente em dispositivos CUDA. Se o seu algoritmo pode obter resultados corretos usando `float32`, então você provavelmente deve usar esse tipo de dados, porque a conversão para `float64` pode, dependendo do tipo de GPU, diminuir drasticamente a função.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Objeto e Modo \"nopython\" \n", - "\n", - "Numba não pode compilar todo o código Python. Algumas funções não têm uma tradução Numba, e alguns tipos de tipos Python não podem ser compilados de forma eficiente (ainda). Por exemplo, Numba não suporta dicionários (no momento da redação deste artigo). Por conta disso, o Numba retornará um modo chamado **modo de objeto**, que não faz especialização de tipo. O modo de objeto existe para habilitar outras funcionalidades do Numba, mas em muitos casos, você deseja que o Numba informe se a inferência de tipo falhar. Você pode forçar o **nopython mode** (o outro modo de compilação).\n", - "Aqui vamos tentar compilar algum código Python que o Numba ainda não sabe compilar, passando o argumento `nopython` para o decorador:\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@jit(nopython=True)\n", - "def cannot_compile(x):\n", - " return x['key']\n", - "\n", - "cannot_compile(dict(key='value'))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Agora, obtemos uma exceção quando o Numba tenta compilar a função e, se você rolar para baixo até o final da saída da exceção, verá um erro que descreve o erro da célula acima:\n", - "```\n", - "- argument 0: cannot determine Numba type of \n", - "```\n", - "**Usar o modo `nopython` é a maneira recomendada e prática recomendada de usar `jit`, pois leva ao melhor desempenho.**\n", - "\n", - "Numba fornece outro decorador `njit` que é um alias para `jit(nopython=True)`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from numba import njit\n", - "\n", - "@njit\n", - "def cannot_compile(x):\n", - " return x['key']\n", - " \n", - "cannot_compile(dict(key='value'))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Acesse a [documentação Numba](https://numba.pydata.org/numba-doc/dev/reference/pysupported.html), para maiores informações sobre funções suportadas em Python." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Introdução ao Numba para a GPU com NumPy Universal Functions (ufuncs)\n", - "\n", - "Começaremos nossa cobertura da programação de GPU no Numba com como compilar [Funções universais NumPy \\(ou ufuncs\\)](https://docs.scipy.org/doc/numpy-1.15.1/reference/ufuncs.html) para a GPU.\n", - "\n", - "A coisa mais importante a saber sobre a programação de GPU quando começamos é que o hardware da GPU é projetado para *paralelismo de dados*. A taxa de transferência máxima é alcançada quando a GPU está computando as mesmas operações em muitos elementos diferentes ao mesmo tempo.\n", - "\n", - "As funções universais do NumPy, que executam a mesma operação em todos os elementos em uma matriz NumPy, são naturalmente paralelas aos dados, portanto, são um ajuste natural para a programação da GPU.\n", - "\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Fazendo ufuncs para a GPU\n", - "\n", - "O Numba tem a capacidade de criar ufuncs *compilados*, normalmente um processo não tão simples envolvendo código C. Com o Numba você simplesmente implementa uma função escalar a ser executada em todas as entradas, decora-a com `@vectorize`, e o Numba descobrirá as regras de transmissão para você. Para aqueles que estão familiarizados com o `vectorize` do NumPy (https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.vectorize.html), o decorador `vectorize` do Numba será muito familiar." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Em nosso primeiro exemplo iremos usar o `@vectorize` decorador para compilar e otimizar a ufunc para **CPU**.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from numba import vectorize\n", - "import numpy as np \n", - "\n", - "@vectorize\n", - "def add_ten(num):\n", - " return num + 10 # This scalar operation will be performed on each element" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "nums = np.arange(10)\n", - "add_ten(nums) # pass the whole array into the ufunc, it performs the operation on each element" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Estamos gerando um ufunc que usa CUDA na GPU com a adição de fornecer uma **assinatura de tipo explícita** e definir o atributo `target`. O argumento de assinatura de tipo descreve quais tipos usar tanto para os argumentos ufuncs quanto para o valor de retorno:\n", - "``` python\n", - "'return_value_type(argument1_value_type, argument2_value_type, ...)'\n", - "```\n", - "\n", - "Consulte os documentos do Numba para obter mais informações sobre [tipos disponíveis](https://numba.pydata.org/numba-doc/dev/reference/types.html), bem como para obter informações adicionais sobre [escrever ufuncs com mais de um assinatura](https://numba.pydata.org/numba-doc/dev/user/vectorize.html)\n", - "\n", - "Aqui está um exemplo simples de um ufunc que será compilado para um dispositivo GPU habilitado para CUDA. Ele espera dois valores `int64` e retorna também um valor `int64`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@vectorize(['int64(int64, int64)'], target='cuda') # Type signature and target are required for the GPU\n", - "def add_ufunc(x, y):\n", - " return x + y" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a = np.array([1, 2, 3, 4])\n", - "b = np.array([10, 20, 30, 40])\n", - "\n", - "add_ufunc(a, b)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Para uma chamada de função tão simples, muitas coisas simplesmente aconteceram! Numba apenas automaticamente:\n", - "\n", - " * Compilou um kernel CUDA para executar a operação ufunc em paralelo sobre todos os elementos de entrada.\n", - " * Memória GPU alocada para as entradas e saídas.\n", - " * Copiou os dados de entrada para a GPU.\n", - " * Executou o kernel CUDA (função GPU) com as dimensões corretas do kernel, considerando os tamanhos de entrada.\n", - " * Copiou o resultado de volta da GPU para a CPU.\n", - " * Devolveu o resultado como um array NumPy no host.\n", - " \n", - "Comparado a uma implementação em C, o acima é notavelmente mais conciso.\n", - "\n", - "Você pode estar se perguntando o quão rápido nosso exemplo simples está na GPU? Vamos ver:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%timeit np.add(b, c) # NumPy on CPU" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%timeit add_ufunc(b, c) # Numba on GPU" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Wow, a GPU é *muito mais lenta* que a CPU?? Por enquanto, isso é esperado porque usamos (deliberadamente) a GPU de várias maneiras neste exemplo. Como usamos mal a GPU ajudará a esclarecer quais tipos de problemas são adequados para a computação da GPU e quais são melhores para serem executados na CPU:\n", - "\n", - " * **Nossas entradas são muito pequenas**: a GPU obtém desempenho por meio do paralelismo, operando em milhares de valores de uma só vez. Nossas entradas de teste têm apenas 4 e 16 inteiros, respectivamente. Precisamos de uma matriz muito maior para manter a GPU ocupada.\n", - " * **Nosso cálculo é muito simples**: enviar um cálculo para a GPU envolve um pouco de sobrecarga em comparação com a chamada de uma função na CPU. Se nosso cálculo não envolver operações matemáticas suficientes (geralmente chamadas de \"intensidade aritmética\"), a GPU passará a maior parte do tempo esperando que os dados se movam.\n", - " * **Copiamos os dados da CPU para a GPU**: embora em alguns cenários, pagar o custo de copiar dados da CPU para a GPU possa valer a pena para uma única função, geralmente será preferível executar várias operações GPUs em sequência. Nesses casos, faz sentido enviar dados para a GPU e mantê-los lá até que todo o nosso processamento seja concluído.\n", - " * **Nossos tipos de dados são maiores que o necessário**: Nosso exemplo usa `int64` quando provavelmente não precisamos dele. O código escalar usando tipos de dados de 32 e 64 bits executa basicamente a mesma velocidade na CPU e, para tipos inteiros, a diferença pode não ser drástica, mas os tipos de dados de ponto flutuante de 64 bits podem ter um custo de desempenho significativo na GPU, dependendo do tipo de GPU. A aritmética básica em floats de 64 bits pode ser de 2x (Pascal-architecture Tesla) a 24x (Maxwell-architecture GeForce) mais lenta do que floats de 32 bits. Se você estiver usando GPUs mais modernas (Volta, Turing, Ampere), isso pode ser muito menos preocupante. O NumPy é padronizado para tipos de dados de 64 bits ao criar matrizes, por isso é importante definir o [`dtype`](https://docs.scipy.org/doc/numpy-1.14.0/reference/arrays.dtypes.html ) ou use o método [`ndarray.astype()`](https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.ndarray.astype.html) para escolher o tipo de 32 bits quando você precisar deles.\n", - " \n", - " \n", - "Dado o exposto, vamos tentar um exemplo que é mais rápido na GPU realizando uma operação com intensidade aritmética muito maior, em uma entrada muito maior e usando um tipo de dados de 32 bits.\n", - "\n", - "**Observação:** Nem todo código NumPy funcionará na GPU e, como no exemplo a seguir, precisaremos usar o `pi` e o `exp` da biblioteca `math` em vez do NumPy. Consulte [os documentos do Numba](https://numba.pydata.org/numba-doc/latest/reference/numpysupported.html) para obter uma ampla cobertura do suporte ao NumPy na GPU.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import math # Note that for the CUDA target, we need to use the scalar functions from the math module, not NumPy\n", - "\n", - "SQRT_2PI = np.float32((2*math.pi)**0.5) # Precompute this constant as a float32. Numba will inline it at compile time.\n", - "\n", - "@vectorize(['float32(float32, float32, float32)'], target='cuda')\n", - "def gaussian_pdf(x, mean, sigma):\n", - " '''Compute the value of a Gaussian probability density function at x with given mean and sigma.'''\n", - " return math.exp(-0.5 * ((x - mean) / sigma)**2) / (sigma * SQRT_2PI)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "# Evaluate the Gaussian a million times!\n", - "x = np.random.uniform(-3, 3, size=1000000).astype(np.float32)\n", - "mean = np.float32(0.0)\n", - "sigma = np.float32(1.0)\n", - "\n", - "# Quick test on a single element just to make sure it works\n", - "gaussian_pdf(x[0], 0.0, 1.0)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import scipy.stats # for definition of gaussian distribution, so we can compare CPU to GPU time\n", - "norm_pdf = scipy.stats.norm\n", - "%timeit norm_pdf.pdf(x, loc=mean, scale=sigma)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%timeit gaussian_pdf(x, mean, sigma)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## CUDA Device Functions\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Allowed Python on the GPU\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercise: GPU Accelerate a Function\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "sTce41AoYIqp" - }, - "source": [ - "## Referências\n", - "\n", - "- https://courses.nvidia.com/courses/course-v1:DLI+C-AC-01+V1/course/\n", - "- https://courses.nvidia.com/courses/course-v1:DLI+C-AC-02+V1/course/\n", - "- https://developer.nvidia.com/blog/how-query-device-properties-and-handle-errors-cuda-cc/" - ] - } - ], - "metadata": { - "colab": { - "authorship_tag": "ABX9TyO3iuMtqTQyr/GMWeimjFKK", - "collapsed_sections": [], - "provenance": [] - }, - "gpuClass": "standard", - "kernelspec": { - "display_name": "Python 3.7.6 ('base')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - }, - "vscode": { - "interpreter": { - "hash": "7fb4e08d40c87d182d3eb80acd71ade9835941d2f88b8731e54a532e624040ce" - } - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} +{"cells":[{"cell_type":"markdown","metadata":{"id":"uBl_9yStW0cz"},"source":["# Introdução ao CUDA\n","\n","Esta pode ser considerada uma introdução mínima ao CUDA e às possibilidades de uso neste ambiente. \n","\n","---\n","## Configurando e instalando \n","\n","Para acessar GPUs NVIDIA no Google Colab é preciso alterar a configuração do ambiente. Para isso, clique no menu superior em **Runtime > Change runtime type**, em **Hardware accelerator** selecione **GPU** e clique em **Save**. Esta configuração é persistente, ou seja, o arquivo já será inicializado neste ambiente da próxima vez que for aberto. \n","\n","O compilador CUDA `nvcc` já vem instalado e pode ser invocado como vemos abaixo, solicitando sua versão com o parâmetro `--version`:"]},{"cell_type":"markdown","metadata":{"id":"OZvaBRGFuDkL"},"source":[]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":460,"status":"ok","timestamp":1667851897572,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"rPPSIrkTVO35","outputId":"d0968ac6-f409-4e1b-e38b-ae9502b591bc"},"outputs":[],"source":["!nvcc --version"]},{"cell_type":"markdown","metadata":{"id":"PkG3-jzTc5P3"},"source":["O comando e `nvidia-smi` _(Systems Management Interface)_ nos dá detalhes do ambiente, tais como aceleradores disponíveis e processos em execução:"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":413,"status":"ok","timestamp":1667851900466,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"5HgjH3lddEaR","outputId":"4db86775-695c-4109-b931-75c3a01339ab"},"outputs":[],"source":["!nvidia-smi"]},{"cell_type":"markdown","metadata":{"id":"n7LU147xdQlI"},"source":["Para facilitar ainda mais, vamos instalar a extensão `nvcc_plugin` que permite escrever e executar código CUDA diretamente nas células do Jupyter:"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":5183,"status":"ok","timestamp":1667851910248,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"DlKyxRLAdXKs","outputId":"697485de-b121-4450-95a8-ee4640a24c3c"},"outputs":[],"source":["!pip install git+https://github.com/andreinechaev/nvcc4jupyter.git\n","%load_ext nvcc_plugin"]},{"cell_type":"markdown","metadata":{"id":"ROGtfdUhdZty"},"source":["Depois disso, basta usar o prefixo `%%cu` no início da célula para rodar código CUDA diretamente no Jupyter sem a necessidade de invocar explicitamente o compilador:"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":2543,"status":"ok","timestamp":1667851912781,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"8dISM5S7VcKI","outputId":"83c00a7e-f0b4-4066-9c9f-f21f3a422e9a"},"outputs":[],"source":["%%cu\n","#include \n","int main() {\n"," int nDevices;\n","\tstd::cout << \"Welcome to CUDA!\" << std::endl;\n"," cudaGetDeviceCount(&nDevices);\n"," for (int i = 0; i < nDevices; i++) {\n"," cudaDeviceProp prop;\n"," cudaGetDeviceProperties(&prop, i);\n"," std::cout << \"Device Number: \" << i << std::endl;\n"," std::cout << \" Device name: \" << prop.name << std::endl;\n"," std::cout << \" Memory Clock Rate (KHz): \" << prop.memoryClockRate << std::endl;\n"," std::cout << \" Memory Bus Width (bits): \" << prop.memoryBusWidth << std::endl;\n"," std::cout << \" Peak Memory Bandwidth (GB/s): \" << 2.0*prop.memoryClockRate*(prop.memoryBusWidth/8)/1.0e6 << std::endl;\n"," }\n","\treturn 0;\n","}\n"]},{"cell_type":"markdown","metadata":{"id":"t2l60EtMeAhj"},"source":["---\n","## Pré-requisitos\n","\n","Para aproveitar ao máximo este laboratório, você já deve ser capaz de:\n","\n","- Declarar variáveis, escrever loops e usar instruções `if/else` em C.\n","- Definir e invocar funções em C.\n","- Alocar arrays em C.\n","\n","Nenhum conhecimento prévio de CUDA é necessário."]},{"cell_type":"markdown","metadata":{"id":"HHO9Azzd5NjS"},"source":["---\n","## Objetivos\n","\n","Ao concluir este laboratório, você será capaz de:\n","\n","- Escrever, compilar e executar programas C/C++ que chamam funções de CPU e **disparam** **kernels** em GPU.\n","- Controlar a **hierarquia de threads** paralela usando a **configuração de execução**.\n","- Refatorar loops seriais para executar suas iterações em paralelo em uma GPU.\n","- Alocar e liberar memória disponível para CPUs e GPUs.\n","- Manipular erros gerados pelo código CUDA.\n","- Acelerar aplicativos somente de CPU."]},{"cell_type":"markdown","metadata":{"id":"HEov_tLu6PJg"},"source":["---\n","## Escrevendo o código da aplicação para a GPU\n","\n","CUDA fornece extensões para muitas linguagens de programação comuns, no caso deste laboratório, C/C++. Essas extensões de linguagem permitem que os desenvolvedores executem facilmente funções em seu código-fonte em uma GPU.\n","\n","Abaixo está um arquivo `.cu` (extensão do arquivo para programas acelerados por CUDA). Ele contém duas funções, a primeira que será executada na CPU, a segunda que será executada na GPU. Gaste um tempo identificando as diferenças entre as funções, tanto em termos de como elas são definidas quanto de como são invocadas."]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":609,"status":"ok","timestamp":1667853393767,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"Ond3PA7O7UYS","outputId":"9f09c8fc-5880-4b50-ad97-b1e67eab2587"},"outputs":[],"source":["%%writefile 01-hello-gpu.cu\n","#include \n","\n","void CPUFunction() {\n"," printf(\"Esta função está definida para ser executada na CPU.\\n\");\n","}\n","\n","__global__ void GPUFunction() {\n"," printf(\"Esta função está definida para ser executada na GPU.\\n\");\n","}\n","\n","int main() {\n"," CPUFunction();\n"," GPUFunction<<<1, 1>>>();\n"," cudaDeviceSynchronize();\n"," return 0;\n","}\n"]},{"cell_type":"markdown","metadata":{"id":"WR3KDWbl7aUk"},"source":["Aqui estão algumas linhas de código importantes para destacar, bem como alguns outros termos comuns usados ​​em computação acelerada:\n","\n","`__global__ void GPUFunction()`\n"," - A palavra-chave `__global__` indica que a seguinte função será executada na GPU e pode ser invocada **globalmente**, o que neste contexto significa tanto pela CPU quanto pela GPU.\n"," - Muitas vezes, o código executado na CPU é chamado de código do **host**, e o código executado na GPU é chamado de código do **dispositivo** ou **acelerador**.\n"," - Observe o tipo de retorno `void`. É necessário que as funções definidas com a palavra-chave `__global__` retornem o tipo `void`.\n","\n","`GPUFunction<<<1, 1>>>();`\n"," - Normalmente, ao chamar uma função para execução na GPU, chamamos essa função de **kernel**, que é **disparada** ou **lançada**.\n"," - Ao lançar um kernel, devemos fornecer uma **configuração de execução**, que é feita usando a sintaxe `<<< ... >>>` antes de passar ao kernel quaisquer argumentos esperados.\n"," - Em um nível alto, a configuração de execução permite que os programadores especifiquem a **hierarquia de threads** para uma inicialização do kernel, que define o número de agrupamentos de threads (chamados **blocos**), bem como quantos **threads** para executar em cada bloco. A configuração de execução será explorada detalhadamente mais adiante no laboratório, mas, por enquanto, observe que o kernel está sendo iniciado com `1` bloco de threads (o primeiro argumento de configuração de execução) que contém `1` thread (o segundo argumento de configuração) .\n","\n","`cudaDeviceSynchronize();`\n"," - Ao contrário de muitos códigos C/C++, o lançamento de kernels é **assíncrono**: o código da CPU continuará a ser executado *sem esperar que o lançamento do kernel seja concluído*.\n"," - Uma chamada para `cudaDeviceSynchronize`, uma função fornecida pelo runtime CUDA, fará com que o código do host (CPU) espere até que o código do dispositivo (GPU) seja concluído e só então retome a execução na CPU."]},{"cell_type":"markdown","metadata":{"id":"HI5h7LvV-XVX"},"source":["---\n","## Experimente você!\n","\n","Abaixo invocamos o compilador `nvcc` para compilar e executar (usando o parâmetro `-run`) o arquivo criado na célula anterior. Execute a célula abaixo para ver o resultado... \n","\n","O parâmetro `-arch=` permite escolher a arquitetura alvo da GPU, experimente trocar para `sm_50` e veja o que acontece. \n","\n","Volte na célula que gerou o arquivo e experimente mudar o número de blocos e threads. Não esqueça de executar a célula para salvar novamente o arquivo."]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":1141,"status":"ok","timestamp":1667853418544,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"IJ5C8pxgWa_5","outputId":"18e45ea5-5eee-43c3-dd50-6dcbab011bfe"},"outputs":[],"source":["!nvcc -arch=sm_70 -o hello-gpu 01-hello-gpu.cu -run"]},{"cell_type":"markdown","metadata":{"id":"TvXwN0OMBG-7"},"source":["---\n","## Índices de threads e blocos\n","\n","Cada thread recebe um índice dentro de seu bloco de threads, começando em `0`. Além disso, cada bloco recebe um índice, começando em '0'. Assim como os encadeamentos são agrupados em blocos de encadeamentos, os blocos são agrupados em uma **grade (grid)**, que é a entidade mais alta na hierarquia de encadeamentos CUDA. Em resumo, os kernels CUDA são executados em uma grade de 1 ou mais blocos, com cada bloco contendo o mesmo número de 1 ou mais threads.\n","\n","Kernels CUDA têm acesso a variáveis ​​especiais que identificam tanto o índice da thread (dentro do bloco) que está executando o kernel, quanto o índice do bloco (dentro da grade) em que a thread está. Essas variáveis ​​são `threadIdx.x` e `blockIdx.x` respectivamente."]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":254,"status":"ok","timestamp":1667853511561,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"9UQu-f1xBVrf","outputId":"8795094d-55b1-4e64-9cc3-b78f17db8334"},"outputs":[],"source":["%%writefile 02-thread-and-block-idx.cu\n","#include \n","\n","__global__ void printSuccessForCorrectExecutionConfiguration() {\n"," if(threadIdx.x == 1023 && blockIdx.x == 255)\n"," printf(\"Success!\\n\");\n","}\n","\n","int main() {\n"," /* This is one possible execution context that will make\n"," * the kernel launch print its success message. */\n"," printSuccessForCorrectExecutionConfiguration<<<256, 1024>>>();\n","\n"," /* Don't forget kernel execution is asynchronous and you must\n"," * sync on its completion. */\n"," cudaDeviceSynchronize();\n","}\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":1081,"status":"ok","timestamp":1667853512934,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"_niddzloB3VK","outputId":"68f795c9-c688-436c-f072-df52e2f14ec6"},"outputs":[],"source":["!nvcc -arch=sm_70 -o thread-and-block-idx 02-thread-and-block-idx.cu -run"]},{"cell_type":"markdown","metadata":{"id":"P4T9a5AFDU6j"},"source":["---\n","## Acelerando loops `for` \n","\n","Loops `for` em aplicativos somente de CPU estão prontos para aceleração: em vez de executar cada iteração do loop em série, cada iteração do loop pode ser executada em paralelo em seu próprio thread. Considere o seguinte loop for e observe, embora seja óbvio, que ele controla quantas vezes o loop será executado, além de definir o que acontecerá para cada iteração do loop:\n","\n","```cpp\n","int N = 2<<20;\n","for (int i = 0; i < N; ++i) {\n"," printf(\"%d\\n\", i);\n","}\n","```\n","\n","Para paralelizar este loop, 2 passos devem ser seguidos:\n","\n","- Um kernel deve ser escrito para fazer o trabalho de uma **única iteração do loop**.\n","- Como o kernel será independente de outros kernels em execução, a configuração de execução deve ser tal que o kernel execute o número correto de vezes, por exemplo, o número de vezes que o loop teria iterado."]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":8,"status":"ok","timestamp":1667853928793,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"Kd67suEUV2jq","outputId":"b40a8d47-98a4-47bb-f57c-7d51516cbbd6"},"outputs":[],"source":["%%writefile 03-single-block-loop.cu\n","#include \n","\n","/*\n"," * Notice the absence of the previously expected argument `N`.\n"," */\n","\n","__global__ void loop() {\n"," /* This kernel does the work of only 1 iteration\n"," * of the original for loop. Indication of which\n"," * \"iteration\" is being executed by this kernel is\n"," * still available via `threadIdx.x`. */\n"," printf(\"This is iteration number %d\\n\", threadIdx.x);\n","}\n","\n","int main() {\n"," /* It is the execution context that sets how many \"iterations\"\n"," * of the \"loop\" will be done.\n"," */\n"," loop<<<1, 10>>>();\n"," cudaDeviceSynchronize();\n","}"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":1060,"status":"ok","timestamp":1667853931782,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"ve8rYwyOWIhx","outputId":"a4dd7ef4-037f-47c9-ec3b-6399bc8ee4eb"},"outputs":[],"source":["!nvcc -arch=sm_70 -o single-block-loop 03-single-block-loop.cu -run"]},{"cell_type":"markdown","metadata":{"id":"760U_zyFWt-j"},"source":["---\n","## Usando dimensões de bloco para mais paralelização\n","\n","Há um limite para o número de threads que podem existir em um bloco de thread: 1024 para ser preciso. Para aumentar a quantidade de paralelismo em aplicativos acelerados, devemos ser capazes de coordenar entre vários blocos de threads.\n","\n","Kernels CUDA têm acesso a uma variável especial que fornece o número de threads em um bloco: `blockDim.x`. Usando essa variável, em conjunto com `blockIdx.x` e `threadIdx.x`, a paralelização aumentada pode ser realizada organizando a execução paralela em vários blocos de vários threads com a expressão idiomática `threadIdx.x + blockIdx.x * blockDim.x `. Aqui está um exemplo detalhado.\n","\n","A configuração de execução `<<<10, 10>>>` lançaria um grid com um total de 100 threads, contidos em 10 blocos de 10 threads. Esperamos, portanto, que cada thread tenha a capacidade de calcular algum índice exclusivo entre `0` e `99`.\n","\n","- Se o bloco `blockIdx.x` for igual a `0`, então `blockIdx.x * blockDim.x` será `0`. Adicionando a `0` os possíveis valores `threadIdx.x` `0` a `9`, então podemos gerar os índices `0` a `9` dentro da grade de 100 threads.\n","- Se o bloco `blockIdx.x` for igual a `1`, então `blockIdx.x * blockDim.x` será `10`. Adicionando a `10` os possíveis valores `threadIdx.x` `0` a `9`, então podemos gerar os índices `10` a `19` dentro da grade de 100 threads.\n","- Se o bloco `blockIdx.x` for igual a `5`, então `blockIdx.x * blockDim.x` será `50`. Adicionando a `50` os possíveis valores `threadIdx.x` de `0` a `9`, podemos gerar os índices de `50` a `59` dentro da grade de 100 threads.\n","- Se o bloco `blockIdx.x` for igual a `9`, então `blockIdx.x * blockDim.x` será `90`. Adicionando a `90` os possíveis valores `threadIdx.x` `0` a `9`, então podemos gerar os índices `90` a `99` dentro da grade de 100 threads."]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":645,"status":"ok","timestamp":1667854117666,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"SuWsdxjQXXu5","outputId":"4cfb4133-8f2b-4123-ed99-6bad17ef3b11"},"outputs":[],"source":["%%writefile 04-multi-block-loop.cu\n","#include \n","\n","__global__ void loop() {\n"," /* This idiomatic expression gives each thread\n"," * a unique index within the entire grid.\n"," */\n"," int i = blockIdx.x * blockDim.x + threadIdx.x;\n"," printf(\"%d\\n\", i);\n","}\n","\n","int main() {\n"," /* Additional execution configurations that would\n"," * work and meet the exercises contraints are:\n"," * <<<5, 2>>>\n"," * <<<10, 1>>> */\n"," loop<<<1, 10>>>();\n"," cudaDeviceSynchronize();\n","}\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":996,"status":"ok","timestamp":1667854120760,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"mox8jBd9Xlka","outputId":"fc146c4a-1020-455b-8813-b8d209bcbea3"},"outputs":[],"source":["!nvcc -arch=sm_70 -o multi-block-loop 04-multi-block-loop.cu -run"]},{"cell_type":"markdown","metadata":{"id":"PiY6WGLKX97C"},"source":["---\n","## Alocação de memória a ser acessada na GPU e na CPU\n","\n","Versões mais recentes do CUDA (versão 6 e posterior) facilitaram a alocação de memória disponível tanto para o host da CPU quanto para qualquer número de dispositivos GPU e, embora existam muitas [técnicas intermediárias e avançadas](http://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html#memory-optimizations) para gerenciamento de memória que oferecerá suporte ao melhor desempenho em aplicativos acelerados, a técnica de gerenciamento de memória CUDA mais básica que abordaremos agora oferece suporte a ganhos de desempenho fantásticos em aplicativos somente de CPU com quase nenhuma sobrecarga de desenvolvedor.\n","\n","Para alocar e liberar memória e obter um ponteiro que possa ser referenciado no código do host e do dispositivo, substitua as chamadas para `malloc` e `free` por `cudaMallocManaged` e `cudaFree` como no exemplo a seguir:\n","\n","\n","```cpp\n","// CPU-only\n","int N = 2<<20;\n","size_t size = N * sizeof(int);\n","\n","int *a;\n","a = (int *)malloc(size);\n","\n","// Use `a` in CPU-only program.\n","free(a);\n","```\n","\n","```cpp\n","// Accelerated\n","int N = 2<<20;\n","size_t size = N * sizeof(int);\n","\n","int *a;\n","// Note the address of `a` is passed as first argument.\n","cudaMallocManaged(&a, size);\n","\n","// Use `a` on the CPU and/or on any GPU in the accelerated system.\n","cudaFree(a);\n","```"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":600,"status":"ok","timestamp":1667854263328,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"vLqWyytXYlyH","outputId":"cc45f5d4-7710-4818-8bbe-d169af0abdca"},"outputs":[],"source":["%%writefile 05-double-elements.cu\n","#include \n","\n","void init(int *a, int N) {\n"," int i;\n"," for (i = 0; i < N; ++i) \n"," a[i] = i;\n","}\n","\n","__global__ void doubleElements(int *a, int N) {\n"," int i;\n"," i = blockIdx.x * blockDim.x + threadIdx.x;\n"," if (i < N)\n"," a[i] *= 2;\n","}\n","\n","bool checkElementsAreDoubled(int *a, int N) {\n"," int i;\n"," for (i = 0; i < N; ++i)\n"," if (a[i] != i*2) \n"," return false;\n"," return true;\n","}\n","\n","int main() {\n"," int N = 1500;\n"," int *a;\n","\n"," size_t size = N * sizeof(int);\n"," /* Use `cudaMallocManaged` to allocate pointer `a` available\n"," * on both the host and the device. */\n","\n"," cudaMallocManaged(&a, size);\n"," init(a, N);\n","\n"," size_t threads_per_block = 256;\n"," size_t number_of_blocks = (N + threads_per_block - 1) / threads_per_block;\n","\n"," doubleElements<<>>(a, N);\n"," cudaDeviceSynchronize();\n","\n"," bool areDoubled = checkElementsAreDoubled(a, N);\n"," printf(\"All elements were doubled? %s\\n\", areDoubled ? \"TRUE\" : \"FALSE\");\n","\n"," /* Use `cudaFree` to free memory allocated with `cudaMallocManaged`. */\n"," cudaFree(a);\n","}\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":1138,"status":"ok","timestamp":1667854271341,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"1I9iWhbUZEN1","outputId":"620591d7-4a76-4942-e73c-bd840eb70646"},"outputs":[],"source":["!nvcc -arch=sm_70 -o double-elements 05-double-elements.cu -run"]},{"cell_type":"markdown","metadata":{"id":"4_hRvjuIZ-tP"},"source":["---\n","## Tratamento de incompatibilidades de configuração de bloco com o número de threads necessários\n","\n","Pode ser que uma configuração de execução não possa ser expressa para criar o número exato de threads necessários para paralelizar um loop.\n","\n","Um exemplo comum tem a ver com o desejo de escolher tamanhos de bloco ideais. Por exemplo, devido às características de hardware da GPU, os blocos que contêm vários threads que são múltiplos de 32 geralmente são desejáveis ​​para benefícios de desempenho. Supondo que queríamos lançar blocos cada um contendo 256 threads (um múltiplo de 32) e precisávamos executar 1.000 tarefas paralelas (um número trivialmente pequeno para facilitar a explicação), então não há número de blocos que produziria um total exato de 1000 threads na grade, pois não há valor inteiro 32 pode ser multiplicado por exatamente 1000.\n","\n","Este cenário pode ser facilmente resolvido da seguinte maneira:\n","\n","- Escreva uma configuração de execução que crie **mais** threads do que o necessário para realizar o trabalho alocado.\n","- Passar um valor como argumento no kernel (`N`) que representa o tamanho total do conjunto de dados a ser processado, ou o total de threads que são necessários para concluir o trabalho.\n","- Após calcular o índice da thread dentro da grade (usando `tid+bid*bdim`), verifique se este índice não excede `N`, e só execute o trabalho pertinente do kernel se não exceder.\n","\n","Aqui está um exemplo de uma maneira idiomática de escrever uma configuração de execução quando tanto `N` quanto o número de threads em um bloco são conhecidos e uma correspondência exata entre o número de threads na grade e `N` não pode ser garantida. Ele garante que sempre haja pelo menos tantos encadeamentos quantos forem necessários para `N`, e apenas 1 bloco adicional de encadeamentos extras, no máximo:\n","\n","```cpp\n","// Suponha que `N` seja conhecido\n","int N = 100000;\n","\n","// Suponha que desejamos definir `threads_per_block` exatamente como `256`\n","size_t threads_per_block = 256;\n","\n","// Certifique-se de que haja pelo menos `N` threads na grade, com apenas 1 bloco excedente\n","size_t number_of_blocks = (N + threads_per_block - 1) / threads_per_block;\n","\n","some_kernel<<>>(N);\n","```\n","\n","Como a configuração de execução acima resulta em mais threads na grade do que `N`, deve-se tomar cuidado dentro da definição de `some_kernel` para que `some_kernel` não tente acessar elementos de dados fora do intervalo, ao ser executado por um dos tópicos \"extras\":\n","\n","```cpp \n","__global__ some_kernel(int N) {\n"," int idx = threadIdx.x + blockIdx.x * blockDim.x;\n"," if (idx < N) { // Verifica se `idx` mapeia para algum valor dentro de `N`\n"," // Só executa em caso verdadeiro\n"," }\n","}\n","```"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":5,"status":"ok","timestamp":1667854596982,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"cb5iL-65cCDN","outputId":"b4625ce3-ff0a-4e5d-ec97-f644c13168c3"},"outputs":[],"source":["%%writefile 06-mismatched-config-loop.cu\n","#include \n","\n","__global__ void initializeElementsTo(int initialValue, int *a, int N) {\n"," int i = threadIdx.x + blockIdx.x * blockDim.x;\n"," if (i < N) \n"," a[i] = initialValue;\n","}\n","\n","int main() {\n"," /* Do not modify `N`. */\n"," int N = 1000;\n","\n"," int *a;\n"," size_t size = N * sizeof(int);\n","\n"," cudaMallocManaged(&a, size);\n","\n"," /* Assume we have reason to want the number of threads\n"," * fixed at `256`: do not modify `threads_per_block`. */\n"," size_t threads_per_block = 256;\n","\n"," /* The following is idiomatic CUDA to make sure there are at\n"," * least as many threads in the grid as there are `N` elements. */\n"," size_t number_of_blocks = (N + threads_per_block - 1) / threads_per_block;\n","\n"," int initialValue = 6;\n","\n"," initializeElementsTo<<>>(initialValue, a, N);\n"," cudaDeviceSynchronize();\n","\n"," /* Check to make sure all values in `a`, were initialized. */\n"," for (int i = 0; i < N; ++i) \n"," if(a[i] != initialValue) {\n"," printf(\"FAILURE: target value: %d\\t a[%d]: %d\\n\", initialValue, i, a[i]);\n"," cudaFree(a);\n"," exit(1);\n"," }\n"," printf(\"SUCCESS!\\n\");\n"," cudaFree(a);\n","}\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":1226,"status":"ok","timestamp":1667854599183,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"gWq-PRRyclAg","outputId":"b1da9072-1423-4a19-a84a-63ae6a1c0ee4"},"outputs":[],"source":["!nvcc -arch=sm_70 -o mismatched-config-loop 06-mismatched-config-loop.cu -run"]},{"cell_type":"markdown","metadata":{"id":"Cz1hUbv3dJuH"},"source":["---\n","## Conjuntos de dados maiores que a grade\n","\n","Por opção, geralmente para criar a configuração de execução com melhor desempenho, ou por necessidade, o número de threads em uma grade pode ser menor que o tamanho de um conjunto de dados. Considere uma matriz com 1000 elementos e uma grade com 250 threads (usando tamanhos triviais aqui para facilitar a explicação). Aqui, cada thread na grade precisará ser usado 4 vezes. Um método comum para fazer isso é usar um **grid-stride loop** dentro do kernel.\n","\n","Em um loop grid-stride, cada thread calculará seu índice exclusivo dentro da grade usando `tid+bid*bdim`, realizará sua operação no elemento naquele índice dentro da matriz e, em seguida, adicionará ao seu índice o número de threads na grade e repete isso, até que esteja fora do alcance da matriz. Por exemplo, para uma matriz de 500 elementos e uma grade de 250 threads, a thread com índice 20 na grade seria:\n","\n","- Realize sua operação no elemento 20 do array de 500 elementos\n","- Incrementar seu índice em 250, o tamanho da grade, resultando em 270\n","- Realize sua operação no elemento 270 do array de 500 elementos\n","- Incrementar seu índice em 250, o tamanho da grade, resultando em 520\n","- Como 520 agora está fora do alcance do array, o encadeamento interromperá seu trabalho\n","\n","CUDA fornece uma variável especial que fornece o número de blocos em uma grade, `gridDim.x`. Calcular o número total de threads em uma grade é simplesmente o número de blocos em uma grade multiplicado pelo número de threads em cada bloco, `gridDim.x * blockDim.x`. Com isso em mente, aqui está um exemplo detalhado de um loop grid-stride dentro de um kernel:\n","\n","\n","```cpp\n","__global__ void kernel(int *a, int N) {\n"," int indexWithinTheGrid = threadIdx.x + blockIdx.x * blockDim.x;\n"," int gridStride = gridDim.x * blockDim.x;\n"," for (int i = indexWithinTheGrid; i < N; i += gridStride) {\n"," // do work on a[i];\n"," }\n","}\n","```"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":234,"status":"ok","timestamp":1667855181942,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"4iMHr2Kvdv8C","outputId":"ea601fe5-cdd7-4765-94a4-7b87e9b24e1c"},"outputs":[],"source":["%%writefile 07-grid-stride-double.cu\n","#include \n","\n","void init(int *a, int N) {\n"," int i;\n"," for (i = 0; i < N; ++i) \n"," a[i] = i;\n","}\n","\n","__global__ void doubleElements(int *a, int N) {\n"," /* Use a grid-stride loop so each thread does work\n"," * on more than one element in the array. */\n"," int idx = blockIdx.x * blockDim.x + threadIdx.x;\n"," int stride = gridDim.x * blockDim.x;\n","\n"," for (int i = idx; i < N; i += stride) \n"," a[i] *= 2;\n","}\n","\n","bool checkElementsAreDoubled(int *a, int N) {\n"," int i;\n"," for (i = 0; i < N; ++i) \n"," if (a[i] != i*2) \n"," return false;\n"," return true;\n","}\n","\n","int main() {\n"," int N = 10000;\n"," int *a;\n","\n"," size_t size = N * sizeof(int);\n"," cudaMallocManaged(&a, size);\n","\n"," init(a, N);\n","\n"," size_t threads_per_block = 256;\n"," size_t number_of_blocks = 32;\n","\n"," doubleElements<<>>(a, N);\n"," cudaDeviceSynchronize();\n","\n"," bool areDoubled = checkElementsAreDoubled(a, N);\n"," printf(\"All elements were doubled? %s\\n\", areDoubled ? \"TRUE\" : \"FALSE\");\n","\n"," cudaFree(a);\n","}\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":1288,"status":"ok","timestamp":1667855794012,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"E54xcvLxeDoZ","outputId":"14d22251-9eff-4e6b-dfe0-6cb5e07c9c58"},"outputs":[],"source":["!nvcc -arch=sm_70 -o grid-stride-double 07-grid-stride-double.cu -run"]},{"cell_type":"markdown","metadata":{"id":"3gN2npqAetcR"},"source":["---\n","## Tratamento de erros\n","\n","Como em qualquer aplicativo, o tratamento de erros no código CUDA acelerado é essencial. Muitas, se não a maioria das funções CUDA (veja, por exemplo, as [funções de gerenciamento de memória](http://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__MEMORY.html#group__CUDART__MEMORY)) retornam um valor do tipo `cudaError_t`, que pode ser usado para verificar se ocorreu ou não um erro ao chamar a função. Aqui está um exemplo onde o tratamento de erros é executado para uma chamada para `cudaMallocManaged`:\n","\n","```cpp\n","cudaError_t err;\n","err = cudaMallocManaged(&a, N) // Assumindo a existência de `a` e `N`.\n","\n","if (err != cudaSuccess) { // `cudaSuccess` é fornecido pelo CUDA.\n"," printf(\"Erro: %s\\n\", cudaGetErrorString(err)); // `cudaGetErrorString` é fornecido pelo CUDA.\n","}\n","```\n","\n","O lançamento de kernels, que são definidos para retornar `void`, não retorna um valor do tipo `cudaError_t`. Para verificar os erros que ocorrem no momento da inicialização do kernel, por exemplo, se a configuração de inicialização estiver incorreta, o CUDA fornece a função `cudaGetLastError`, que retorna um valor do tipo `cudaError_t`.\n","\n","```cpp\n","/* Este lançamento deve causar um erro, mas o próprio kernel não pode devolver. */\n","algumKernel<<<1, -1>>>(); // -1 não é um número válido de threads.\n","\n","cudaError_t err;\n","err = cudaGetLastError(); // `cudaGetLastError` retornará o erro acima.\n","if (err!= cudaSucess) {\n"," printf(\"Erro: %s\\n\", cudaGetErrorString(err));\n","}\n","```\n","\n","Finalmente, para capturar erros que ocorrem de forma assíncrona, por exemplo, durante a execução de um kernel assíncrono, é essencial verificar o status retornado por uma chamada de API de tempo de execução CUDA de sincronização subsequente, como `cudaDeviceSynchronize`, que retornará um erro se um dos kernels lançados anteriormente falhar."]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":12,"status":"ok","timestamp":1667862142223,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"v6a6qaxOfrZP","outputId":"545a519b-65e4-4d23-c7bf-3cfdc34b38d9"},"outputs":[],"source":["%%writefile 08-add-error-handling.cu\n","#include \n","\n","void init(int *a, int N) {\n"," int i;\n"," for (i = 0; i < N; ++i)\n"," a[i] = i;\n","}\n","\n","__global__ void doubleElements(int *a, int N) {\n"," int idx = blockIdx.x * blockDim.x + threadIdx.x;\n"," int stride = gridDim.x * blockDim.x;\n","\n"," /* The previous code (now commented out) attempted\n"," * to access an element outside the range of `a`. */\n","\n"," // for (int i = idx; i < N + stride; i += stride)\n"," for (int i = idx; i < N; i += stride)\n"," a[i] *= 2;\n","}\n","\n","bool checkElementsAreDoubled(int *a, int N) {\n"," int i;\n"," for (i = 0; i < N; ++i)\n"," if (a[i] != i*2) \n"," return false;\n"," return true;\n","}\n","\n","int main() {\n"," int N = 10000;\n"," int *a;\n","\n"," size_t size = N * sizeof(int);\n"," cudaMallocManaged(&a, size);\n","\n"," init(a, N);\n","\n"," /* The previous code (now commented out) attempted to launch\n"," * the kernel with more than the maximum number of threads per\n"," * block, which is 1024. */\n"," size_t threads_per_block = 1024;\n"," /* size_t threads_per_block = 2048; */\n"," size_t number_of_blocks = 32;\n","\n"," cudaError_t syncErr, asyncErr;\n","\n"," doubleElements<<>>(a, N);\n","\n"," /* Catch errors for both the kernel launch above and any\n"," * errors that occur during the asynchronous `doubleElements`\n"," * kernel execution. */\n","\n"," syncErr = cudaGetLastError();\n"," asyncErr = cudaDeviceSynchronize();\n","\n"," /* Print errors should they exist. */\n","\n"," if (syncErr != cudaSuccess) printf(\"Error: %s\\n\", cudaGetErrorString(syncErr));\n"," if (asyncErr != cudaSuccess) printf(\"Error: %s\\n\", cudaGetErrorString(asyncErr));\n","\n"," bool areDoubled = checkElementsAreDoubled(a, N);\n"," printf(\"All elements were doubled? %s\\n\", areDoubled ? \"TRUE\" : \"FALSE\");\n","\n"," cudaFree(a);\n","}\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":2029,"status":"ok","timestamp":1667862144244,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"xWsKz0tigKkF","outputId":"4c214c0b-5360-4117-fe20-f8ab395db02a"},"outputs":[],"source":["!nvcc -arch=sm_70 -o add-error-handling 08-add-error-handling.cu -run"]},{"cell_type":"markdown","metadata":{"id":"kItQSgU4gYls"},"source":["---\n","## Resumo\n","\n","Neste momento, você atingiu todos os seguintes objetivos de laboratório:\n","\n","- Escrever, compilar e executar programas C/C++ que chamam funções de CPU e **lançamento** de **kernels** de GPU.\n","- Controlar a **hierarquia de threads** paralela usando a **configuração de execução**.\n","- Refatorar loops seriais para executar suas iterações em paralelo em uma GPU.\n","- Alocar e liberar memória disponível para CPUs e GPUs.\n","- Manipular erros gerados pelo código CUDA.\n","\n","Agora você concluirá o objetivo final do laboratório:\n","\n","- Acelerar aplicativos somente de CPU."]},{"cell_type":"markdown","metadata":{"id":"-N1D207PlMym"},"source":["***\n","## Sua vez! \n","\n","O código serial a seguir realiza a soma de dois vetores. Tente modificar o código com o que você aprendeu até agora para torná-lo paralelo. \n","\n","Para facilitar, eu coloquei na célula seguinte as mudanças necessárias. "]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":7,"status":"ok","timestamp":1667862151419,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"7K3bRuQmlizD","outputId":"e738de14-8777-43f5-9997-3654d5e2e044"},"outputs":[],"source":["%%writefile 09-vector-add.cu\n","#include \n","\n","void initWith(float num, float *a, int N)\n","{\n"," for(int i = 0; i < N; ++i)\n"," {\n"," a[i] = num;\n"," }\n","}\n","\n","void addVectorsInto(float *result, float *a, float *b, int N)\n","{\n"," for(int i = 0; i < N; ++i)\n"," {\n"," result[i] = a[i] + b[i];\n"," }\n","}\n","\n","void checkElementsAre(float target, float *array, int N)\n","{\n"," for(int i = 0; i < N; i++)\n"," {\n"," if(array[i] != target)\n"," {\n"," printf(\"FAIL: array[%d] - %0.0f does not equal %0.0f\\n\", i, array[i], target);\n"," exit(1);\n"," }\n"," }\n"," printf(\"SUCCESS! All values added correctly.\\n\");\n","}\n","\n","int main()\n","{\n"," const int N = 2<<20;\n"," size_t size = N * sizeof(float);\n","\n"," float *a;\n"," float *b;\n"," float *c;\n","\n"," a = (float *)malloc(size);\n"," b = (float *)malloc(size);\n"," c = (float *)malloc(size);\n","\n"," initWith(3, a, N);\n"," initWith(4, b, N);\n"," initWith(0, c, N);\n","\n"," addVectorsInto(c, a, b, N);\n","\n"," checkElementsAre(7, c, N);\n","\n"," free(a);\n"," free(b);\n"," free(c);\n","}"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":966,"status":"ok","timestamp":1667862154545,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"C8tLlSqQNLX8","outputId":"a23cd7ae-561e-4f8d-a1a8-d98146453a57"},"outputs":[],"source":["!nvcc -arch=sm_70 -o vector-add 09-vector-add.cu -run"]},{"cell_type":"markdown","metadata":{"id":"ObZV-RVaVO2L"},"source":["Abaixo, por meio do comando `diff`, temos as principais mudanças elencadas entre a implementação serial e a paralelizada."]},{"cell_type":"markdown","metadata":{"id":"DYkmlJPqieUf"},"source":["```diff\n","1a2,11\n","+ #include \n","+ \n","+ inline cudaError_t checkCuda(cudaError_t result)\n","+ {\n","+ if (result != cudaSuccess) {\n","+ fprintf(stderr, \"CUDA Runtime Error: %s\\n\", cudaGetErrorString(result));\n","+ assert(result == cudaSuccess);\n","+ }\n","+ return result;\n","+ }\n","10a21\n","+ __global__\n","13c24,27\n","- for(int i = 0; i - N; ++i)\n","---\n","+ int index = threadIdx.x + blockIdx.x * blockDim.x;\n","+ int stride = blockDim.x * gridDim.x;\n","+ \n","+ for(int i = index; i - N; i += stride)\n","41,43c55,57\n","- a = (float *)malloc(size);\n","- b = (float *)malloc(size);\n","- c = (float *)malloc(size);\n","---\n","+ checkCuda( cudaMallocManaged(&a, size) );\n","+ checkCuda( cudaMallocManaged(&b, size) );\n","+ checkCuda( cudaMallocManaged(&c, size) );\n","49c63,72\n","- addVectorsInto(c, a, b, N);\n","---\n","+ size_t threadsPerBlock;\n","+ size_t numberOfBlocks;\n","+ \n","+ threadsPerBlock = 256;\n","+ numberOfBlocks = (N + threadsPerBlock - 1) / threadsPerBlock;\n","+ \n","+ addVectorsInto<<>>(c, a, b, N);\n","+ \n","+ checkCuda( cudaGetLastError() );\n","+ checkCuda( cudaDeviceSynchronize() );\n","53,55c76,78\n","- free(a);\n","- free(b);\n","- free(c);\n","---\n","+ checkCuda( cudaFree(a) );\n","+ checkCuda( cudaFree(b) );\n","+ checkCuda( cudaFree(c) );\n","```"]},{"cell_type":"markdown","metadata":{"id":"WnHPXbHmf4Pn"},"source":["___\n","# CUDA C/C++ vs. Numba vs. pyCUDA\n","\n","A maneira mais comum de programar em CUDA é com as extensões de linguagem CUDA C/C++. Com relação ao Python, o pyCUDA é, além do Numba, uma alternativa para acelerar o código Python em GPUs. Vale a pena fazer uma rápida comparação das três opções mencionadas antes de começarmos:\n","\n","### CUDA C/C++:\n","\n","- A maneira mais comum, eficiente e flexível de utilizar CUDA\n","- Acelera aplicações C/C++\n","\n","### pyCUDA:\n","\n","- Expõe toda a API CUDA C/C++\n","- É a opção CUDA com melhor desempenho disponível para Python\n","- Requer escrever código C em seu Python e, em geral, muitas modificações de código\n","\n","### Numba:\n","\n","- Potencialmente menos desempenho que pyCUDA\n","- Não expõe (ainda?) toda a API CUDA C/C++\n","- Ainda permite aceleração massiva, geralmente com muito pouca modificação de código\n","- Permite aos desenvolvedores a conveniência de escrever código diretamente em Python\n","- Também otimiza o código Python para a CPU"]},{"cell_type":"markdown","metadata":{"id":"CiQXKG2YNLYA"},"source":["## Primeiros passos: Compilação para CPU\n","Se você se lembra, o Numba pode ser usado para otimizar o código para uma CPU ou GPU. Como introdução, e antes de passar para a aceleração de GPU, vamos escrever nossa primeira função Numba e compilá-la para o **CPU**. Ao fazer isso, teremos uma leve introdução na sintaxe do Numba e também teremos a oportunidade de comparar o desempenho do código Numba otimizado por CPU com o código Numba acelerado por GPU.\n","\n","O compilador Numba normalmente é ativado aplicando um [**function decorator**](https://en.wikipedia.org/wiki/Python_syntax_and_semantics#Decorators) a uma função Python. Decoradores são modificadores de função que transformam as funções Python que eles decoram, usando uma sintaxe muito simples. Aqui usaremos o decorador de compilação da CPU do Numba `@jit`:\n","\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":1180,"status":"ok","timestamp":1667862842824,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"JKFNQNhWglH7","outputId":"622990b3-f5bb-48b3-fc36-4e8023461a60"},"outputs":[],"source":["from numba import jit\n","import math\n","\n","# This is the function decorator syntax and is equivalent to `hypot = jit(hypot)`.\n","# The Numba compiler is just a function you can call whenever you want!\n","@jit\n","def hypot(x, y):\n"," # Implementation from https://en.wikipedia.org/wiki/Hypot\n"," x = abs(x);\n"," y = abs(y);\n"," t = min(x, y);\n"," x = max(x, y);\n"," t = t / x;\n"," return x * math.sqrt(1+t*t)\n","\n","hypot(3.0, 4.0)"]},{"cell_type":"markdown","metadata":{"id":"uuuKWB8fNLYE"},"source":["Entraremos em mais detalhes abaixo sobre o que acontece quando `hypot` é chamado, mas por enquanto saiba que na primeira vez que chamamos `hypot`, o compilador é acionado e compila uma implementação de código de máquina da função para entradas float. O Numba também salva a implementação original do Python da função no atributo `.py_func`, para que possamos chamar o código original do Python para ter certeza de obter a mesma resposta:"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":11,"status":"ok","timestamp":1667862966577,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"P3o6HQ4bNLYF","outputId":"b11f9cec-9d85-43ca-a2bf-20ae787d9e9d"},"outputs":[],"source":["hypot.py_func(3.0, 4.0)"]},{"cell_type":"markdown","metadata":{"id":"UNaHccJiNLYF"},"source":["### Teste de Desempenho\n","\n","Uma parte importante do uso do Numba é medir o desempenho do seu novo código. Vamos ver se realmente aceleramos alguma coisa. A maneira mais fácil de fazer isso em um notebook Jupyter, como aquele em que esta sessão é executada, é usar a [`%timeit` função mágica](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-timeit). Vamos primeiro medir a velocidade do Python original:\n","\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":1950,"status":"ok","timestamp":1667862955716,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"TPhwhI22NLYG","outputId":"2a67e192-82bf-4d4a-e8d8-f2af4e53559e"},"outputs":[],"source":["%timeit hypot(3.0, 4.0)"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":10865,"status":"ok","timestamp":1667862966575,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"rVtmMkaINLYH","outputId":"84740aba-1170-4bf9-e073-eea639379eff"},"outputs":[],"source":["%timeit math.hypot(3.0, 4.0)"]},{"cell_type":"markdown","metadata":{"id":"AeAMoiSGNLYH"},"source":["O built-in do Python é ainda mais rápido que o Numba! Isso ocorre porque o Numba introduz alguma sobrecarga para cada chamada de função que é maior que a deixa mais sobrecarregada que a chamada de função do próprio Python. Funções extremamente rápidas (como a acima) serão prejudicadas por isso. (Como um teste, se você chamar uma função Numba por meio de outra função, haverá muito pouca sobrecarga de função, às vezes até zero se o compilador inserir a função na outra. Em resumo, sempre faça benchmark de suas funções para obter evidências de aceleração.)"]},{"cell_type":"markdown","metadata":{"id":"M5P-dCKfNLYI"},"source":["### Exercício: Uso do Numba para compilação de uma função para CPU\n","\n","A função a seguir usa [o método de Monte Carlo para determinar o Pi](https://academo.org/demos/estimating-pi-monte-carlo/). \n","A função em si já está funcionando, então não se preocupe com os detalhes matemáticos da implementação.\n","\n","Complete as tasks para compilar `monte_carlo_pi` com Numba antes de executar as 3 células a seguir que irão:\n","\n"," 1. Confirme se a versão compilada está se comportando da mesma forma que a versão não compilada.\n"," 2. Faça um benchmark da versão não compilada.\n"," 3. Faça um benchmark da versão compilada.\n","\n","\n"]},{"cell_type":"code","execution_count":null,"metadata":{"executionInfo":{"elapsed":820,"status":"ok","timestamp":1667863371689,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"jZi5c8f0NLYI"},"outputs":[],"source":["# TODO: Import Numba's just-in-time compiler function\n","from numba import jit\n","import random\n","\n","# We will use numpy's `testing` library to confirm compiled and uncompiled versions run the same\n","from numpy import testing\n","\n","nsamples = 1000000\n","\n","\n","# TODO: Use the Numba compiler to compile this function\n","@jit\n","def monte_carlo_pi(nsamples):\n"," acc = 0\n"," for i in range(nsamples):\n"," x = random.random()\n"," y = random.random()\n"," if (x**2 + y**2) < 1.0:\n"," acc += 1\n"," return 4.0 * acc / nsamples\n","\n","# This assertion will fail until you successfully complete the exercise one cell above\n","testing.assert_almost_equal(monte_carlo_pi(nsamples), monte_carlo_pi.py_func(nsamples), decimal=2)\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":8930,"status":"ok","timestamp":1667863380616,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"BRJLLVckNLYJ","outputId":"208f7862-b8d3-4c06-ad9c-af646e34ee2a"},"outputs":[],"source":["%timeit monte_carlo_pi(nsamples)"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":2771,"status":"ok","timestamp":1667863383381,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"X3fAdm4lNLYK","outputId":"83bab8ac-1393-412c-a743-49e1002579f8"},"outputs":[],"source":["%timeit monte_carlo_pi.py_func(nsamples)"]},{"cell_type":"markdown","metadata":{"id":"VhysMowdNLYM"},"source":["## Funcionamento do Numba\n","\n","Agora que tivemos o primeiro contato usando o compilador Numba, vamos dar um olhada no que realmente está ocorrendo por baixo dos panos. A primeira vez que chamamos nossa função `hypot` encapsulada em Numba, o seguinte processo foi iniciado:\n","\n","\n","\n"]},{"cell_type":"markdown","metadata":{"id":"lwnH3PcZaLK_"},"source":["![numba_flowchart.png](data:image/png;base64,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yAUHHhd2H4UIIIAAAgggUDYCBGDKxpmzIIAAAggggAACCCCAAAIIIIAAAqUSyMrLlaU7NlivSA0N6HCEPHDc4EhV2IcAAggggAACZSBAAKYMkDkFAggggAACCCCAAAIIIIAAAggg4LdAw6q15J5j/i7nH9jH71PRPgIIIIAAAghEIZASMCmKelRBAAEEEEAAAQQQQAABBBBAAAEEEChDgd05WfLF4tmyeudGWbtjs3nfLGvM+xrznpqSKg2q1ZL6VWtKm7pN5MT9e1hrxaSlppbhFXIqBBBAAAEEEIgkQAAmkg77EEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAIESCPCziBKgcQgCCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggEEmAAEwkHfYhgAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAiUQIABTAjQOQQABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQiCRCAiaTDPgQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEECgBAIEYEqAxiEIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQCQBAjCRdNiHAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCJRAgABMCdA4BAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBCIJEAAJpIO+xBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQACBEggQgCkBGocggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAApEECMBE0mEfAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIFACAQIwJUDjEAQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAgkgABmEg67EMAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEESiBAAKYEaByCAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCEQSSI+0k30IIIAAAggggAACCCCAAAIIIIAAAghEEsjKypJff/1VVq5cab3Wrl0rderUkWbNmrletWrVitQM+xBAoIIKbNiwQWbNmiVz586VW265pYL2km45Bb7//nv5448/pHv37tK5c2epVKmSc3dSbacETEqqHtNZBBBAAAEEEEAAAQQQQAABBBBAYB8JnHDCCbJmzZrg2YcMGSL6SsS0dOlSefbZZ+Wll16STZs2FduFo48+WqZNm1ZsvUSpcOutt8qkSZOCl3vMMcfIc889F8yzgUC8BL744gu58cYbXc1Nnz5dqlSp4iorL5l58+bJuHHjZPbs2VbgZfXq1dal6UP47Ozs8nKZSXMdd999t3zwwQfB/h555JHywgsvBPN+bIwcOVJuvvlmq+mMjAzp0qWLdOvWTY466ii58MILy+1n1w8LRsD4oUqbCCCAAAIIIIAAAggggAACCCCAQBgB/UXw8uXLg3v0l+GJmJ5++mkZOnSo5OXlRX35OlKmIqUVK1bI/Pnzg11q2bJlcJsNBOIpsH37dtdnTdsuj7+p179vw4cPl7ffflvy8/PjSUBbpRBYtWqV6/PTtGnTUrQW+6EadNMRUPp6+eWXrc+IBoUuv/zypBgZwxowsX9mOAIBBBBAAAEEEEAAAQQQQAABBBBISgF96HvDDTfI9ddfH1PwJSmx6DQCSSKgo+EGDx5sTTX11ltvEXxJkvte0m5qQOiaa66RDh06yCuvvFLh/y1hBExJPykchwACCCCAAAIIIIAAAggggAACCCSZgI58eeqpp0J6fdhhh0nPnj2tV48ePawHsDrtkI4S0WnHdAolEgIVWeCOO+6QnJycYBfPPfdc6/sQLKigG2PHjrWCL86+O7uqoy10+kH75dzHdsUVuPTSS6V169by3Xffia4HM2PGjJDp5zRwd9lll8no0aOt6Rzr169fIUEIwFTI20qnEEAAAQQQQAABBBBAAAEEEEAAgfgKLFmyRPQhszPpAzOdUqZfv37OYmu7a9eu1rtOM6Np7dq11jv/QaAiCjzxxBPinGavU6dOFT4AM2bMGGskQ7jpxs466yx54IEHRB1IySdQt25d0c+AvjTpd0PXBdJ/Q7z/Fuh6Rn369JHPP/9cmjRpUuGwmIKswt1SOoQAAggggAACCCCAAAIIIIAAAgjEX+Dhhx+WXbt2BRtOTU2V999/P2zwJVjJsVERH6w5uscmAkkl8OSTT8pVV10VMt2YBl6nTp0q48ePJ/iSVJ+IyJ2tUqWK6KgYXSdo2LBhkpGR4Tpg3rx50rt3b1m5cqWrvCJkCMBUhLtIHxBAAAEEEEAAAQQQQAABBBBAAAGfBSZNmuQ6wxVXXGE9MHMVkkEAgQov8NBDD8nQoUND+nn//fdbC60fd9xxIfsoQEAFatasKSNGjJD58+dbawY5VTQ406tXL1m8eLGzOOG3CcAk/C2kAwgggAACCCCAAAIIIIAAAggggIC/AgsXLpRly5a5TjJgwABXngwCCFR8gYkTJ8qdd94Z0tEHH3xQ7rnnHklPZ8WLEBwKQgTatWtnrQ3WoUMH1z5dF0b/bcnNzXWVJ3KGAEwi3z2uHQEEEEAAAQQQQAABBBBAAAEEECgDgR9++MF1Fp1ORqeLISGAQPII6BSE1157bUiH77vvvpD1oUIqUYCAR0Cnpfzyyy+lbdu2rj1z5swRXVOpoiQCMBXlTtIPBBBAAAEEEEAAAQQQQAABBBBAwCeBTZs2uVrWB2cahCEhgEDyCGigZfny5a4O33TTTTJ8+HBXGRkEohVo3ry5FYRp2rSp6xD9THlHXboqJFCGAEwC3SwuFQEEEEAAAQQQQAABBBBAAAEEENgXAlu2bHGdtk6dOq48GQQQqNgCOirhySefdHVSp5HS9WBICJRGoFWrVvLYY4+5mtDRVtddd52rLFEzTMqXqHeO60YAAQQQQAABBBBAAAEEEEAAgXIlsG3bNmvxYF1AeMmSJbJhwwapWrWq1K9fXxo1aiT6C9/s7Oy4XfPXX38tU6ZMkb/++st6bdy40TrPfvvtJ6eeeqr079/fOnc8Trh161ZXMxU9AJOVlWXdS72Pej9XrVolGRkZov3We6kjgLxBKRdQlJmcnBzR+/jxxx/L77//LuvWrRO1rlevnnWOgw46SM444ww54ogjJDW1/P6OWtdt2L59e7DXnTp1kkqVKgXz+vD+P//5j8yYMUPatGkjgwYNkosvvji437mhFs8884z8+eef0r59e/nHP/5hrQnhrBPL9u7du13fy9WrV0vlypWlbt260rhxY8tZv6MpKSnBZvU+60PhREzq9uqrr1qLnP/222/W53b//feX0047TS688ELrb1Ks/crPz5crr7wyZF2OUaNGWZaxtlfS+vp90c+a/b3U0Tj6vahVq5Z1L/V+6istLS14Cv3eduzYMZhnI7KAfmb08zN37lz5448/RINsRx11lPTq1Uv69OkT+eBS7NW/CWPGjLH+HtrN6HpD77//vgwcONAuKtW7/l3XtYo+++wz2bNnj9Wff/3rX9bfglI1XNzBARICCCCAAAIIIIAAAggggAACCCCAQIkFzMPlwPnnnx8wD/0C5jlMTC8zpU9M583LywuMGzcu0KNHj2LPU7169YD5dXrAPGiK6RzhKl9wwQWu8/Xr1y9ctYQvMw/nA7fcckugRo0arv5Gc19POumkqPtvFpgOPP300wHz4D+q85igWuDtt9+Ouv2yrnj66ae7+mEejAcvwQRTAuYhuGu/ev7zn/8M1rE3tCyctVkPwq4S9buZvihg1isJmKnywrYZ7jx22XnnnRf1eeyK3vO8+OKL9q5Sv48fPz6kDyaw5GrXBPAC5iF2xL9DDRo0CEyYMMF1XDSZyZMnh5zfBHijOTQudUxwO2AenAcaNmwYch32PSvqXb87yZ4uvfRSl9vxxx8fQmKCowET7A2YQKSrrtNV/53bsWNHyLHxKpg3b14gPT3ddf7u3bvHpXkTvAuYQLarbe1b69atAyaYHpdzFNVI+Q2dGwESAggggAACCCCAAAIIIIAAAgggUF4F9Jf0OsqkW7duYoIiYoIjvl7qzp07xQQ+xDwEk+nTpxd7Lp3C5Y477hATGBA9tjRJf3nuTM7RAs7yRN3WX0Nff/311ugMnQqntF6RHFauXCmHHnqoNb2Od22doo7TtRBMUMC6l+YBaFHVyl35pEmTrH6GG/n1yCOPWKNc7It+4IEHZMSIEXbW9X7//fdLZmamq6yojH7ur7jiCuuX+6NHjxb91XtFTyaoYn2m3nrrrYh/h3SU3IABA0KmEivOZ+zYsa4q5iF5mS2SriMWdDTSnXfeaY0qdF0ImbgI6IgzHWX34YcfigkiFNmm/jt32GGHWaOriqxUih1dunSRIUOGuFqYNWuWLFiwwFVWksyzzz4rP/74Y8ihOqJK1zbyMxGA8VOXthFAAAEEEEAAAQQQQAABBBBAoEIK6LRUOiWLTpcU6YFVvDq/Zs0aOfbYY+XTTz8NaVKn4NEpdjQQ5Jz2ya6oU1ydcsopER/M2nV1PQcNrnhf77zzjl3Fev/oo49C6niPsfPRBhlcJyjDjD6w1+mZzIgUa1oaP0+9aNEiOeaYY0Sn5AqXdCqsww8/XGrWrBlutzXlXN++faW8m+rFr1ixQv7+97+LTl8VLmnA8vnnn7d2vfHGG3L33XeHq2aVbd68Wd57770i99s7dFq4E044QV566SXxBg3tOqV51we19ufa++4N9GgQyFsnUj7aAJP3+jVAoVM06ec4mqR/r2677TaZPXt2NNVFp3DTAI8z6WdQp5LzM+nnRg3vuusu0ekdSfEX0O/I4MGDxYwUi/r7olMl6g8PwgVV43GFl19+eUgz3gBgSIUoCv73v/8VWSvSviIPimEHa8DEgEVVBBBAAAEEEEAAAQQQQAABBBBAYP78+XLiiSeKjoDxpkMOOcR6gN6zZ08x04RZD/T1obv9MtNIRf1LfrttfbCrDzz1wZcz6ZoyTz31lJx88sliphuzdulIjs8//1z0Idb69euD1adNmyaPPvqoDBs2LFjGhljruOh6OeF+Gd22bVtr7QO9l/pSY/s+6rsG33RtmGiTjqrRe6WjWZxJgy66PooG2HS9BU368Fl/9a1r/OgDaOfDfR39pA/cv/zyy3K9LowGVOx1ctTPTCUkr732muvzr+uV6MP1m2++OUjSu3dv6dq1q7zyyiuuoIL38x88YO+Grp+j38tff/3Vu0sOOOAA173UQKXzXmqAQddsSrT0wgsvWJ8P53XraJGLLrrI6q+uVaTBU13TY+3atcFq+uBdRyBp4Ku49MEHH4SMCDvnnHOKO6xU+/X6zLSH8u6774a0o3/3NIhpfy91zRcNiNv3U78zxX1WQhpN0gL93r388suu3h944IHW3yl7pIv+m+X1VG8dXXbTTTe5jo1HRte96tChg7X+jN3em2++KTpCTgOYJU1misAiD420r8iDYtlR1NxklCOAAAIIIIAAAggggAACCCCAAAIIuAV03QWzoHXIPPK63op5mOmuHCZnHo66jh0+fHiYWu6icOtimBEbATOdkLuiI2emuQqYh9iuc+kaFeYhtaNW6Kaus2CeK8X1Fek6Q6+gbEvOPPPMkL6ah3wBM91RQNdpiZTOPfdc17EmuBKpeuCyyy5z1VdnM3IpYIIQEY/74YcfAuYhc8ixur5PeUneNWDM4tkBMzLLumYTDAxepnctEzNqy1qnyP7M/d///V+wrm7b5fpuRkME94Xb+Nvf/uaqr8foukz6mTYBrXCHBMtM4MZ1rK51ES7de++9rnrO6yvttndNF+f5vW56LhNUda1vo9433HBD2DU6TNA4ULlyZde1161bN2ACHc7ThN02AUrXcbpGhwlWha0br8Jwf/O0z7oWlRnpE/E03nuka3wkewq3BoyuJeb8zFarVi0wcuTIgK4z5ky65ku471a9evUCZmSas2rctk3w1nVtep1mlEqp2jdBnJA27f6bKQ5L1XZxBzMCxkiTEEAAAQQQQAABBBBAAAEEEEAAgWgETMDE+rW1s67+uv79998Xnb8+3mnmzJnWyBVnu+aBouhaD0VNU6V1mzdvLqNGjbJGztjH6igKncrFOdrA3me/68iDiy++2M4G37/55hvXyI0WLVq42g5WDLNhHvyGKd33RTrqQX/d70x16tSxRmnoWjvxTDrCxvtL806dOsn3338ves5ISddm+Omnn6wRJPaIEq2v02FdcskloiMCylvS9Tp0FM9RRx0luvaCnUygRvTzoCO1NOmoILNYvbWtUyE5pyEzC4LLPffcY+3T/0QaoaK2X331VbCubjRq1Mj6nujosXilgw8+OOz3Q9vX75b22U5HHnmktG/f3s4W+26CRcXWcVbQtaDskVH6t0BHZOnooXCpc+fO8q9//UtMYCO4Wz9L+rnU0SRFJR2dpCNKnKlPnz7SoEEDZ1Fct3UEk47Wc6aMjAwxwYGQ9UGcddiOXkDXPdHp3eyk/6boyEl7BJ5dru81atSQzz77zJrmUo+zk04LqCMwTcDLLorbu46w+ve//+1qT0dD6bSfJU2DBg2ypvD0TmemIw/9HhlKAKakd43jEEAAAQQQQAABBBBAAAEEEEAgqQR0zYTHH3/c1edmzZrJzz//LLVq1XKVxyujD7fMSIxgczoFi07NFCn4Ylc2v1qW448/XqZOnWoXyeuvvx4xAKOBh3DBB10A3jl1lk61ptMaJWravn27tTi88/r1Abiul6MP2eOdvA+UtX0NNhQXfLGvY7/99hMzusF6iG6X6RoMum6NTs1T3pI+RNfPqj6gda5LpNtm5EVwOiydykiDFrVr15YRI0a4uqFTszlTUeuA6FR7t956q7OqFeTR4JZOIxfPpAvY6ytc0qm+7ICI7tdpAMOtZxHu2JKUmVFu1mE6NZ6uDRUpkKIV9TvsDMBo2fLly/WtyKT3x/n3Rysed9xxRdYv7Q79LPzjH/8IOadOlXb22WeXtnmO3yug07XZSYP1Op1hpDV9NGg6dOhQ62Ufp+9FrWXlrFOSbZ2GTIN8ZvRk8PCFCxcGt0u6of/+aRB40qRJVhBYA5YaiIo1+Bnr+VNjPYD6CCCAAAIIIIAAAggggAACCCCAQDIK6K/xvQ8jzdQlvgVf9OGT99fnJ510UpG/cg93T3TNEWfSB+M66iDZkwYGvGv4mCnCfAm+rFmzJmQRcw0M6APxWJIGYPTX6M703HPPiS5mXx6TjqTSQJ03abDFTvaIEV3npmHDhnax9e4M3Lh2eDK6lon+Gt+Zrr/++rgHX5ztl5dtfTBupnsrNvii16trw+hIEmfatGmTMxuy7Qy62jt19JtfSUelaUDbmTSwRPDFKRK/bf3OffHFFxGDL/bZNJjo/fuzZMkSe3fc372fs3CfxZKcVP/u6og5XVfmqquu8j34otdIAKYkd4pjEEAAAQQQQAABBBBAAAEEEEAgqQR0up7Jkye7+qxTjpm59V1l8czolCu6GLUzFfXre2cd57ZZr8aZtUYbhFuk3FUpCTI6hZszmfUPXKNLnPtKu61TY9mBBrutW265JeYHf2bNBde0QdqWBh5mzZplN1tu3nX0iwZVwiW1dibN64NQbzJrjXiLQvJm7QXRRcKdSUfY6BRoyZB0FJVOCRZNMmvEhDxAd47YCddGuBEyOurPr+T9Xup5NMBG8kdAA7g6hWY0SYMv3hFlOkLKr6Qjc5wp3GfRub88bxOAKc93h2tDAAEEEEAAAQQQQAABBBBAAIFyIWAWwQ4JhuiUSX5OXaK/BncmfYCq62LEkrwBGD123rx5sTRR4eqaBclFX8500003iV8Pls3i0c5TWduHHnpoSFk0BeGOC9d+NG35WUdHXhW1/okGZ5xJ13so6RR+Os3YihUrnM1ZwRcNwlT0pNM0xbp2RZUqVVwsxY2eCjfqwK/viY7M06nUnGngwIGiayCR4i+gawjFGtD3jkrRaQGd61LF8yq9n7PMzMyI60DF89zxbqv4UHK8z0h7CCCAAAIIIIAAAggggAACCCCAQIIJeH9lr9MjnXLKKb72wjlPv55I13rwrnVR3AXs2LEjpIpfD8xCTlROC7z3Ui+zf//+vl3tggULQtpubRa9LkkKd1y49kvSdjyP0enSok3OxcCjPcauV9b30j5veXgfM2aMa32daK5JpyxzJu+Uis59uh1u1IF3ZIL3mJLmP/roI9GH7M7k5/fSeZ5k29apx3QaxlhTuHu/atUqa12nWNsqrn64c+nn0TtVYXHtlIf9BGDKw13gGhBAAAEEEEAAAQQQQAABBBBAoFwL/Pjjj67r0wfhfo5+0amV9MGWM2kwRRcRLm3SBeiTOXnvpVq0a9fONxLvOhtVq1aVRo0aleh8++23X8hx3vZDKuyDAp2eL5qkv3IvbvH4SO1476V+JyMtJh6prUTbpyNgYk3ev1nFjYDJzs4OOUU0U8OFHBRFgfde6iF+fi+juKQKW+Xggw+2FrmPtYPez48er1Mj+pHCfc7CfR79OHe822QKsniL0h4CCCCAAAIIIIAAAggggAACCFQoAZ0axzuSxO8Hg+vWrRO/HjZ5+1KhblYUnVm9erWrlk5X5ddDRD2RN0Cii6GXNGnAwvtg0tt+SdveF8d5F/WO9Rq891JtdXQaKT4C4T6rXvP4nEkkXLt+/52N17UnSzt//fWXq6s6LWbjxo1dZfHKhPs8hAtAx+t8frbDCBg/dWkbAQQQQAABBBBAAAEEEEAAAQQSXmDNmjUhffAuRhxSoZQF+mDLmzp37iyHHXaYtzjmfDzaiPmk5egA7/30+156AyalCazpiAXvqAVv++WI2tdLyc/PFw1UOpPf99J5rmTYDvfAWx+M++Hs/V7WrFmzxCPFkuHe7Is+eqfF1OBLuFEx8bg27whQDaw2adIkHk2XeRsEYMqcnBMigAACCCCAAAIIIIAAAggggEAiCYT7JW5JpznSqcWiSTpFlS6YnZWVFayuC7C/8sorwTwbsQuo5+bNm10H+n0v69ev75pObuXKlaLBg3BBNteFhcnogvPez5C2n4xpw4YN4l3DxO97mWzO+3IETLj1jqLx934/ojmGOsUL5OTkyLJly1wVdUSeX8n7727Lli1L9DfTr+uLpd3Qn1PEcjR1EUAAAQQQQAABBBBAAAEEEEAAgQousGvXrpAehisLqeQp+PLLL0UfvjtTpIeF+sDJmbzHOvexHZ3A7t27QyqW5F7qw8HPP//c1VZR97JBgwauevog0/tw0VUhQsb7AFSretuPcHiF2hXuvoUrK67TOq3Sd99956pW1L10VUqCTLgRMN6RCfFi8N47bz6a82iA9Z133nFV5V66OEqcWbp0acjou+OOO67E7RV3oPdzFi4YWFwb5WU/AZjycie4DgQQQAABBBBAAAEEEEAAAQQQKJcC4R5CxhoM0YeJV1xxRcjohUgd9k7zM3/+/JAHYJGOZ1+ogK714l13JNZ7qa1effXVsnXr1tAThCk55JBDQkrDBVJCKoUpCHdcuPbDHFrhilq0aBHyi/hY76U+nB88eLCEC8zFCpaRkeE6xDl6zbUjgTLhRhT98MMPvvTA+3fW+wA+mpPee++9snDhwmiqUidGgXCjL88777wYW4muuv6d8wap999//+gOLoe1CMCUw5vCJSGAAAIIIIAAAggggAACCCCAQPkRCDcVTqwPB4cNGyZLliyJqVNnn322q/769etDRl24KpCJSsD7UDnWezl27Fj5+OOPozqXVjr22GND6n711VchZdEUhDsuXPvRtJXodTTg0bx5c1c3Yr2Xo0ePlv/973+uNkqa8Y5E2rJlS0mbKjfH6VSIRxxxhOt6Pv3007gErFyNmoz3e7lnzx7ZuHGjt1qR+Z9++kkef/zxIvezo+QCGtgcOXKkq4EOHTqIX8Hf9957z3UuzfTv3z+kLFEKCMAkyp3iOhFAAAEEEEAAAQQQQAABBBBAYJ8IVK9ePWQx6J9//tm1PkukC9NfDj/zzDORqoTdd84550jVqlVd+/773/+68mRiF/A+6N20aZMsWLAgqoZ++eUXufbaa6Oqa1fSAEm1atXsrPX+5JNPxvwQW6cAeuutt1ztaF8OOOAAV1kyZbz3Uo2WL18eFcHXX38tt99+e9i6eXl5YcsjFXrX4tHPVUVIF154oasbOlrok08+cZXFI+O9l9pmtMExvec6GiPcfQtXFo/rTaY27rnnHsnMzHR12fu5cO0sZcY7jZyOXDzllFNK2eq+O5wAzL6z58wIIIAAAggggAACCCCAAAIIIJAgAt6Hg7oAuI6EKC6NGzdOLr/88pimHrPbrFWrlpx11ll21np/9913ZdKkSa4yMrEJeO+lHh3NL+fnzJkjJ510kmzfvj2mE9auXVsuvfRS1zH6y/4xY8a4yorL/Oc//wlZdP7GG28s7rAKvd97L/Vh+6hRo4rts06j1a9fv5CHyvaBsd5jPc47Aubbb7+1m0vodw1spKenu/oQboSCq0IJMt57qU1E871cs2aN6Fok4abn0zZKci/1ODvpWk/6vW/Xrp0MHDhQPvzwQ3tXUrzr5/i1115z9VVHhd58882usnhl9D7qDxyc6dxzzxXvFH/O/bFs//bbb/LUU0/Jo48+KhpQL4tEAKYslDkHAggggAACCCCAAAIIIIAAAggktMBRRx0Vcv36AGfnzp0h5XaBjla56KKLJD8/3yo69NBDpUmTJvbuqN7vu+++kFEw//jHPyTWtS70ZLoOzbPPPiuLFi2K6twVtVK4e/nGG29EdJk2bZqccMIJYk8rpaMdvFMzRfK66aabQh5iP/zww1GvV6Hn945+0mvQ4F4yp3D38sUXX5RIU5HpA3X9Nb393W3WrJkcffTRLsZt27a58tFkjjzySFe1GTNmiAbtEj1pYOnkk092dUNHwNjfBdeOUmTULyUlxdXCd999J1OnTnWVOTO63osGX+y/aZUqVQoJWu/YsaNEAXA9j36WTjzxRJkyZYr89ddfMn78eDnzzDNFR4QkQ9KRm8cff3zw3zC7z/rviI4M9SPp32Jvitdom8cee0wOOuggueGGG+S2226Tnj17Wtve88U7TwAm3qK0hwACCCCAAAIIIIAAAggggAACFU7gjjvuEB2R4kz68M/5UN7ep8GRU0891Xo4npubaxXXrVtXdPSK91e83geOdhv2u86z/9BDD9lZ613b1wdH0f56V6dluvXWW0UXLR8yZIhs3rzZ1V6yZXRqN+/aBbpguk4V5p2KTINWOsqkd+/eoqOeNOk9e/3116VVq1Yuukj3Un89P3z4cFf9tWvXWkGcL774wlXuzeivz/Uhs66J4Uz6ELRGjRrOoqTbHjx4sLRv397Vbw2e9OrVSxYvXhxSfsUVV1gP1O0Ai47sePvtt6Vbt26uuiUZNaGfK2/Sh9ezZs3yFrvy+jdCR8ppkK68Ju8DcP1exDsIcfDBB1vTiHkNNODhDcLoSCcNYOp90xENdnrkkUdkwIABdtZ6DwQCokGYWNPWrVtl6NChYQ/797//LeHWYwpbOQELNTip91ynW8zOznb1QMu9ATlXhVJk9G+i3ldn0pFR4QKtzjrRbH/zzTdW0MX+N9k+RkfDeIPb9r54vbvHj8WrVdpBAAEEEEAAAQQQQAABBBBAAAEEKpBAw4YNZdiwYXLXXXe5evXjjz+KBkl0ihp9IK+/2NbFoJ0Py/XB/KuvvhqyyLSroQgZ/bXuBx98ILpmhZ102h39xbg+nLz66qutB5H6S/WcnBzr1+D6UPL3338XnWpJF822R+HYxyfzu94PfVCrQQ1nUtMePXpI3759rQCNPjjXkSf6INaZ7rzzTmsEha7tE0vSIJ7+kt65roW2rQ8z9UGnBoAOP/xwa2F5DR7oZ0vr62fHmy677DIJ98DfW6+i5zWAMmLECGtqKGdflyxZIvpAX+/xgQceKNOnT7e+m94H8RrcPOaYY6z77Dy+JEHKjh07WgE1vW920qnm9PN09913W/e2e/fu1npAK1assEY/6d+K559/3hrR5h2FY7dRHt7POOMMaW2mndJgrp2ee+450YCWN3hl7y/J+4MPPmiNMnE+9Ndgj45Y0vukLw1867RY+rDemXR6MA2WOv9O2vv1fnoD6Pa+ot5nzpxpjRosar/+HdC/r4mY9O+ajuDTwHDbtm2tdw1U6dRf+tKRW857YPdRp+174YUX7Gzc33VUijf4qfc0UnA72ot48803ixwJpaNuNJjrWzK4JAQQQAABBBBAAAEEEEAAAQQQQACBYgTM4tMBM4okYB7SRP0y07QEzILCwZZNkMZ1rBkVEdwXacM8lAqYUTWuY73XoecyD6Qj1tFjzEPfSKcKu8/Mwe9qt3///mHrJVLhaaed5uqT19ObNw8BA/fee2/ABLOsbnpNTCCl2O6bkRcBMzoj4nmrVasWcf+gQYMCJtBW7LnKssLpp5/uumazKHqRpzejj4J1TfCyyHrmoXmwnt6LPn36FFnXBC9cdb33zptPS0sLmCBcsD3zADbk+NWrVwf3R7thAmcBs2B4SFv2+fW8VatWDbtf+1BUMlNfhRyjf49iTeaBu6udaP/+6HlMINd1rPZJr9n+PsR6LUXVv+WWW0LOY/sV9X7JJZcEzCLxVpNmqrCQ49Uv1mSCPCHteM9vRorE2uw+qW/WoCq2L96+efMXXHCBr393TGA65BpNwDJgRqzExUz/zfL2yc537tw5LucoqhGmIDPSJAQQQAABBBBAAAEEEEAAAQQQQKA4AfPg1PrltY6SiCbpr4v11/DxGKlQs2ZN+eijj+T6668v8tT6S3Hv9CrOyi1bthQdhaEjdkhiTftkghlRUeiv53UU0r/+9a9S/Rpb25k8ebKYh5lFntc8WA+7E9Zs5gAAQABJREFUT0d73H777TJ27NiQ9WTCHpBEhRMnThT9dX40SdfO0Xug0/LZSUfJeJOOTok16XRJ+j31Tk9nt6NTZ5lAgZ11vaemlu/HtDoKRRdDdyYd8acjeOKZdAoqHW0YzagHXfNFp5DS0WhVqlSxLkPvgXdqvpLcy2jW7PJOcxdPh/LSlq6RNHLkSGvaRf0b5EfSKc90ekxn0u/DmDFjxAQtncUl3vZOO+lsKNI+Z72Sbpfvb3ZJe8VxCCCAAAIIIIAAAggggAACCCCAgA8COg2PPnT0PixynkrXWtHFfnUKq65duzp3lWpbH0SNGjXKCuqcd955UT2E1weR5tfhouuM6PRBOsVPnTp1SnUdFeVgtdFpaUaPHh2yNo/dRw18mV/kW+tMmF9Q28WletdAngZRdCoynVKpuAfN+tDTjNaR2bNnW+sjlPcH9aXCKeHB+pn+8MMPrenIinpgq+sw6RSCus6Pd/o5nT7Muz6TcyqxWC5LpxKbN2+etbh3NN81Df488cQTMmHChFhOs0/q6nXWrl3bde7rrrtOzCg/V1lpMnr/dFo5vZ96z8IlvVc6Dd+vv/4qen5n0u+T9+9uSe6lfk81uGQHdpznsLd12sKKmPRvTpcuXUSnmdMgk66FU9zfqZI6aMDZjKCzvjPONnRaxmh/7OA8rqht7YP+2+xNZuSoDPesz+WtU9p8ig6NKW0jHI8AAggggAACCCCAAAIIIIAAAggkm4Cu46DBmO+//95aj8BMY2Kth6APy/WX2X4nM82TFVjR9S70IZmuc6C/vNdfgNsvfYhmprTy+1ISvn210/Uc9F7q2jm6LoI+xNWgSzQP0UsDoA9xP/nkE+u869ats9ac0VEajRs3loMOOkjM1HNFPogOd9777rtP7r///nC74lamI3nshezj1micGtqwYYN1L/W7aaajkvbt21vrwOi99I6MiNMpi2xG19HQBeQ1AGCmNBO9v2qn91Y/YyeeeKJ1fUU2UA53PPPMM9aaRc5L0wf2GoQ566yznMWl3tZRfTp6Rb+XGoDUB+j6N02/E82bNy91+9E0MH/+fGskhp7fuX6THqtlutZQIiZdn0jX0Vm/fr1s2bJFsrKyREe76L8dOlqyqEBmPPuq59Tgi/5AwJn03uo6ZhoAj2fSfzOvueYa63w6Gq1nz57y7LPPWn9n43keb1sEYLwi5BFAAAEEEEAAAQQQQAABBBBAAAEEEEhQgWQPwCTobUuYyzZrvlhT6I0bN851zRp0fvvtt+MehHGdZB9mNIh25JFHuq5g06ZNYtb8cZWRiU5Ag2tnn322TJo0yXWABknNekNi1spylcczY9bQEg3ARBrdFM/zMQVZPDVpCwEEEEAAAQQQQAABBBBAAAEEEEAAAQQQqKACOgXeG2+8YU0B5uyiPtQeMGCAtebVsmXLnLsqxLZOK+dMOnUcwRenSHTbOhmXTsGoa5F5gy862vDzzz/3NfiiV6nBwrIKvuj5/Fk5R1smIYAAAggggAACCCCAAAIIIIAAAggggECZCuiUSBdffLGv5yzLh5e+doTGSySgQZiXXnrJmt5Q11Bypvfee08mTpwot912m/zzn/+sMFMgzp0719lNaxSQq4BMsQK//PKL3HjjjdYUgd7KDRo0kMmTJ8shhxzi3ZXweaYgS/hbSAcQQAABBBBAAAEEEEAAAQQQQAABBBBAAIGyF7j99tvlkUceCXtiXcNDp+065phj5Oijj5bDDz9cdNHzREurVq2y1oTaunWrdenarz/++EOaNGmSaF0p0+vVtcl0LaZp06ZZL13XJdxy9OqoayXp+j4VMRGAqYh3lT4hgAACCCCAAAIIIIAAAggggAACCCCAAAJlIKBrdtxzzz0yc+bMiGerVq2a6NofiZZOO+00a10S+7pHjRol119/vZ3lPYzAU089JTfccEOYPYVFlStXlquvvlruuOMOady4ceGOCrbFGjAV7IbSHQQQQAABBBBAAAEEEEAAAQQQQAABBBBAoKwETj31VJkxY4aMHz/eGilS1Hl1nZhESosWLZIzzjjDFXw5/vjj5dprr02kbuyTa83NzS3yvLoGiwZe1PeJJ56o0MEXRSAAU+RHgR0IIIAAAggggAACCCCAAAIIIIAAAggggAAC0QicddZZMmfOHHnzzTelX79+0rJly2gOK5d17r33Xmnfvr189NFHwevr1auXfPDBB6Jr4JBiE9B1o3r06GGNHFq4cKE8++yz0qJFi9gaSdDa6Ql63Vw2AggggAACCCCAAAIIIIAAAggggAACCCCAQDkS0ODEoEGDrJde1ubNm62gzOzZs2X+/Pnl6EojX8q6deuCFdLS0qwRGyNGjEjINWyCHSnDjXbt2sltt90m3bp1k4MPPlg6duwo6piMiTVgkvGu02cEEEAAAQQQQAABBBBAAAEEEEAAAQQQQACBsAJXXXWVfPbZZ9K3b19rLZNDDjkkbD0KEShOgABMcULsRwABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEgage3bt0utWrWSpr901D8BAjD+2dIyAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIJKkAKwYl6Y2n2wgggAACCCCAAAIIIIAAAggggAACCCCAAAIIIOCfAAEY/2xpGQEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBJJUgABMkt54uo0AAggggAACCCCA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ARiXgg0EEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAIDoCBGCi40gtCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggIArQADGpWADAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEIiOAAGY6DhSCwIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCDgChCAcSnYQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQSiI5AVnWqoBQEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQACB6ArkLPWPIchsmBvdC1AbAjEU8P/1xvBCVI0AAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIpIsAAZh0udP0EwEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBOImQAAmbtRcCAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBNJFgABMutxp+okAAggggAACCCCAAAIIIIAAAggggAACCCCAAAJxEyAAEzdqLoQAAggggAACCCCAAAIIIIAAAggggAACCCCAAALpIkAAJl3uNP1EAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQACBuAkQgIkbNRdCAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQACBdBEgAJMud5p+IoAAAggggAACCCCAAAIIIIAAAggggAACCCCAQNwECMDEjZoLIYAAAggggAACCCCAAAIIIIAAAggggAACCCCAQLoIZKVLR+knAggggAACCCCAAAIIIIAAAggggAACCMRGIHDur76KrQktfPvsIIAAAukowAiYdLzr9BkBBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQRiKkAAJqa8VI4AAggggAACCCCAAAIIIIAAAggggAACCCCAAALpKEAAJh3vOn1GAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQACBmAoQgIkpL5UjgAACCCCAAAIIIIAAAggggAACCCCAAAIIIIBAOgoQgEnHu06fEUAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAIKYCBGBiykvlCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggkI4CBGDS8a7TZwQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEIipAAGYmPJSOQIIIIAAAggggAACCCCAAAIIIIAAAggggAACCKSj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jg7TSgQQQAABBBBAAAEEEEAAAQQQCBEgABNCkjwZc+bMkR49ehTa4EAgIKVKlZIyZcpIrVq1pHnz5nLZZZdJo0aNCj032gVycnLEsizJygr903v44Yfl/fffty/5z3/+U3r16hXty1MfAghESWD79u1y6qmnlri2wYMHS8eOHUtcTzJXkJubK/pszM7ODunGk08+KePGjbPzu3btKnfccUdIGTISWyBagZjE7iWtQwABBBBAAAEEEEAAAQQQQACBcAKhb8HDlSIvIQU2b94sX3/9dbHa1r9/f/ulZ+/eveWcc84pVh1FPWn69OnSs2dPO8hSr169kNN///13tz/HH398yHEyEEAgcQQ0aFDc54+3F2vWrPHupt32jBkz7Ofi2LFjpXHjxiH9X7Rokevcpk2bkONkJI8AgZjkuVe0FAEEEEAAAQQQQAABBBBAAIFoCbAGTLQkk6weHYUyZcoUOe+882TixIkxbb2+qL3uuuukffv2Mnv27Jhei8oRQACBZBDQZ7COYDz22GNl5syZydBk2hglAQ3EsEZMlDCpBgEEEEAAAQQQQAABBBBAAIEEF2AETILfoKI07/nnn5emTZuGPWX37t2iUwb9+eefMmLECPntt9/schoc0enIdHRKixYtwp5b0sydO3fKyy+/XNJqOB8BBBJYoG/fvsWakqxVq1YJ3KvYNU2fvS+88EKhF6hevbo7Mka3SakjwIiY1LmX9AQBBBBAAAEEEEAAAQQQQACB/AQIwOQnk4T5xx13nLRu3brQluuvrnUdAQ3YaNq0aZPcdtttMmnSpELPpQACCCAQTqBly5Zy2mmnhTtEXgkE7r//ftF/pNQVIBCTuveWniGAAAIIIIAAAggggAACCCDAFGRp+DdQtmxZee655+TSSy91e68jYHQRaBICCCCAAAIIxF+Aqcnib84VEUAAAQQQQAABBBBAAAEEEIi1AAGYWAsncP3du3d3W7dlyxb55Zdf3P1E3NApe7Sd0Ui7du0SnZYt2mnHjh3RrpL6EEAghgK6Fku0vrc6zaM+WxIlJfJzbvPmzYnClHDtCAnEJFwLaRACCCCAAAIIIIAAAggggAACCEQqQAAmUqkULHfQQQf5euUsBD179my56KKL7H8XX3yxrFmzxlcuvx1dz8A575VXXpGvvvrK3r/88st9p9xwww1uuR9++MF3LHhH16zRtSVOPvlkqVKlilSsWFEqVaokp59+uowePVr05WkkyVlv4bzzzpNDDjlEypUrJxUqVJA2bdrIlVdeKS+99JJomYLSf/7zH7fdP/74o1108eLF8tBDD0nnzp2lRo0aUr58eXstnSuuuELGjx9fUHUcQwCBMAIPP/yw+z177733wpTwZz3wwANu+Y8++sh/0OzpVIvOc2nevHn28d9//10GDx4sZ511lui6KvosOPzww+1nwQcffBBSR34ZGrgZNWqUvfZN7dq17eeKjjBs3LixdOrUSSZOnBhyqo421PZccsklvmO9evVy2/ndd9+5x3T9LKf9L774opsfbiORn3PLli2TO++8U9q1a2c/y/U5XrVqVTn++OPl+uuvF+1bIgWvwvnGO88JxMT7ulwPAQQQQAABBBBAAAEEEEAAAQSiKGBeYCd8ChzwjOX9l/ANjlMDzYs8jT64/0zgpEhXnjVrlnuu1vPuu+/a55tfcVsm2OEeGz58eKH1munLrHr16rnnfPrpp9brr7/u7nvb6d02Lyjduq+55hq3vHlRZ02ePNmqWbOmm+c9z9k2gQ/LjIpx6wi38cUXX1hmbZwC69H6TjzxRGvhwoXhqrDzbrnlFreOjz/+2FK/WrVquXlOm7yfJthkqScpT8D7XdbtdE17lgQs779kc9Dvnfdv/bXXXotKF0zgwq338ccfL7TOjh07uuXDPau8z5Vp06ZZX3/9tVWtWjX3HG8fnG2zJpZlggEFXtsESayGDRsWWI/W1759e2vVqlVuXW+99Vah57zzzjtu+Ztvvtkt37NnTzc/eCORn3NmyksrKyvL7YfjHPzZqFEjq6j/HQt22F/73u+ybpMQQAABBBBAAAEEEEAAAQQQQAABFWAEjHkDlK5JR7p4k/4CXFOZMmV868OYQIq3WNhtE3CRv/76yz524IEHSocOHexflh911FHStm1b3zkmGCKar/8qV67sO+bsvP3223LGGWfI6tWr7aw6depIly5d7F9PlypVyikm77//vtxxxx3ufvCGeSlpt8UZsZKRkSGHHXaYXHXVVaKjVHQ0TCAQsE/TETutWrUSE1wJriZk//PPP7fr/fvvv8W8WLTrPPvss6Vu3bq+siNHjhTzMteXxw4CCOwfgQ8//NAerbJu3TrJzs6Wli1bin5vdfSKNz311FPSr18/b5ZvW+sxAVtZunSp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K5oXpRFAAAEEEEAAAQQQQAABBBBIFgECMMlyp5KknboWizNFUUmbvH37dtm1a1dJq4na+dqW3bt3R60+p6JoeTn18YlALAT+85//uNWeeeaZ9vYVV1zh5r377rv2aBg3o5gb+r2PNOXk5EhRykdab7TLbdu2TXbu3BlRtdqfaDxn9uzZE/E1I2qYKRTN53sk19y8eXMkxVKmDEGYlLmVdAQBBBBAAAEEEEAAAQQQQAABV4AAjEuRmhsPP/ywXHTRRfa/9957r9BOPvDAA275jz76KKT8888/7x6fN2+effz333+XwYMHy1lnnSW6rkqFChXk8MMPlyuvvFI++OCDkDryy9BAxKhRo+xpjGrXri3lypWTsmXL2mu2dOrUSSZOnBhy6vTp0+32XHLJJb5jvXr1ctv53Xffucd0CiXH48UXX3Tzw20468qcd955csghh9jt0b61adPG7ttLL71U6NoG+tLauZ6OINC0ePFieeihh6Rz585So0YNKV++vD2aQF9mBy9wHq5d5CEQbwENPr7++uvuZZ0AzDHHHCMHHXSQna8v/Av7TjkVPP300+734o8//rADBffcc480a9bM/p41aNBAdP/vv/92TnE/Fy1aJPr91uvq86FixYpyxBFHyE033ST6PND0/fffu/WHG5nz+OOPu8cj+c4V9hzVfjvf87lz59pt+Pnnn+Waa66RmjVr2t9xbWvdunVFF6X/888/7TLO/yxcuNDuU506dez+67OvefPm0qNHD1m9erVTrNDP0aNHy/XXX28veq8u+rzSdcH0Wfzf//7XDqAUVIk+05x+aPs16f0ZMmRI2OfVO++8E7a6WbNmufXo34WTbr/9djdfp68LTsuWLZM777xT2rVrJ1WqVJFKlSpJ1apV5fjjj7f7pc6JFJQPbn809gnCREOROhBAAAEEEEAAAQQQQAABBBBIIAHzi9aET4EDnrG8/xK+wcVo4JYtWyzzZ+H+e+2114pRS+gpJnDh1mleOoYWCMrp2LGjW3748OFBRy3LvFB0j0+bNs36+uuvrWrVqrl53j4427fddptlXpqF1OXNMEESq2HDhgXWo/W1b9/eWrVqlXuqWQS80HPMS0K3/M033+yW79mzp5sfvPHFF19YrVu3dss6fQn+PPHEEy3z8jT4dHf/lltucev4+OOPLfNi0qpVq5abF1yf7t9www2W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506TV0SHH9PXREZGyv33369OEa5s7ty5cuHCBXWOfCT33HOP7urXI8KheTLsS9s///yj8u7oc1fHo0ePOvDCHgPREKZu3LhxJn8P1giPqL/++isQl8s1kQAJkAAJkECiE6AAk+i3gAsgARIgARIgARIgARIgAdcEbt68qV5I4lvUiLnPL6u4ZsUWEiCB2BGoVauWtG/f3lx0/vx5efXVV825LhQqVEiJKPocYaggLLiz/v37y6VLl2TPnj0CMQFCjJ0hDBkMz7r33nvPdLF6vOhKXQcPHGsYNG/yv+gx4ntMmTKlwxDXr193OMdJ/vz5BeG6tMWW19atW6Vo0aL68oA7wtvopZdeMuu6fPmydOnSxZyzQAIkQAIkQAIk8B8BCjD/sWApAQjg20L6o6fT57E56mt5JAESIAESIAESIAF/EujXr58g8TA+1pdN/pxTj3316lXp0KGDZMyYUfLlyyelSpWS7NmzqyTZEGNoJEACJOALAh999JHkyJHDDDV9+nSZN2+eOdeFkSNHCvKZwA4ePKgSy0MUtrNPPvnEwbsGz099rXN/axiyWbNmqeakSZMKxCFn0wIM6vfu3aua4clTp04d565+O8farCLMqlWrbOcCA+TLgcEbqHnz5nL69Gnbvp9++qmMGTPGtCE/j77WVAZY4cMPP3QIEYffGfzu0EiABEiABEiABBwJUIBx5MEzEiABEiABEiABEiABEjAE8IIPYXHwSWjRAwmxv/766xjfMr9165YULFjQrJEFEiABEogPgUyZMgnEFavBmwFeDVZD+K8333zTVM2fP19Kliwpffr0EeQBwXPyu+++k9q1a8vrr79u+tWoUUPefvttc+5cqFKlimTNmtWhumzZsoJ1ORtEGQggVqtbt66DIGJt81dZh0LD+B07dpQHH3xQsM9FixaZKeE1hJBu2hC2zY4XQq1ZvY4eeugh6dGjh74sYI/p0qWTzz//3GF9Xbt2FXhR0UiABEiABEiABP4j4PhfLv/Vs0QCPiWgc7tYj3oCa11sy3oMHkmABEiABEiABEgglAiMGjVKpk2bZraUOXNmqVmzpvpGOF7WpU6d2rSxQAIkQALxJfDUU09JgwYNzDDI52EN8aUbEFYMYo1+Bp06dUo++OADadq0qfIUbNOmjaxcuVJ3l4oVKypvGqvHiGn8XwG5W5544gmHaquni7UBokz58uWtVZKQ4cf0xJ06ddJFuX37tmzcuFFWr14tGzZsMPUowIsS3i3w0oHBA8aZ148//qja8AN7gycJ8qwEgyF8HDx7tOH3wSo66XoeSYAESIAESCCcCVCACee7z72TAAmQAAmQAAmQAAkEJAFr+J9y5cqp8DV4qYkXdUuXLg3INXNRJEACwU3giy++kLRp05pN4BxeLVZD2Gh4OWzfvl0ef/xxh/7WfoULF1beMOvXr5f06dNbm2zL1jBk6ACh2ZVZxZlkyZLFEG9cXefL+pdfflkGDRqkcr1Yx/3jjz+spyr89iuvvCLbtm3zyOvbb7+VX3/9VTJkyOAwRqCfQJBDqExtY8eOdRDhdD2PJEACJEACJBCuBJKF68a5bxIgARIgARIgARIgARIIVAInT540S3vhhRcEoV5oJEACJGBHoHjx4oJIAvG1PHnyxAg75mpMhEFESC3Me+TIEdm5c6ecPXtWCRIFChRQuUEg1nhrTZo08XoPAwcOFHy8tUmTJgk+3hpysVjzsbi6DmHV8Dl37pzKiZMzZ07JlSuXbXdXvJDfC225c+eOEVrNdiAvK7/55hvBx51hXl/83mDfDDvmjjTbSIAESIAEwp0ABZhw/w3g/kmABEiABEiABEiABAKOwLVr18ya8GKORgIkQAKBSAAiC0QEfMLVsmTJIvh4Y+TlDSX2IQESIAESIIHQIsAQZKF1P7kbEiABEiABEiABEiCBACOAbxhfv349zqtKnjx5rK9FToKbN2/G+jpXF/zzzz+ummzrMf+NGzds2+JTGR+O1nkxji/Wd+fOHZ+MY10byyRAAiRAAiRAAiRAAiRAAqFDgAJM6NxL7oQESIAESIAESIAESCARCHz++efSokUL9dm3b59awd69e1XyZeRIyJw5s8qTUKJECUGCaoTtsTMkatbjHD9+3HRBngFd37ZtW1PvXJg8ebK0b99eKlSooEKWIZdD6dKl1ZzILeAp1MyoUaPMPHv27JFbt25J79695YEHHlAJtyMiIuTNN9+UEydOOE+tzqdMmSIvvviiw/ylSpVS80+YMMHj/OPGjTPzI18C7PDhwyrUEJKDZ82aVSWyLlasmDz33HMya9Ys1cebHxCjpk2bJvXq1ZP77rtP7QdJxPPmzSuPPfaYzJ4925thVJ+ZM2dKx44d5cEHH1SckVwb9xZrQtiiu3fvej0WO5IACZAACZAACZAACZAACYQ2AYYgC+37y92RAAmQAAmQAAmQAAn4mcDGjRtlxowZapb/+7//kzNnzggEA+eY+L///rvg891338kbb7whgwcPFqt3C5Iv63GsS167dq05tSY61pWnT59Wwsv8+fN1lTkiUTY+mBMCDUQOCBB2tmnTJjP/66+/Ln369FGihe577NgxGTZsmFSvXl0aN26sq9V+IbzMnTvX1OnCjh07BB/M//3338v48eMF+QLsbOvWrWb+Dh06CM4hmGB/VkOSa3yQ06FTp04yYsQISZkypbWLQxnzI7/EgQMHHOohSB09elR9li1bJhUrVlSiDoQmO8P97Ny5swMT3U/fW51rAoJXZGSkbuaRBEiABEiABEiABEiABEggTAlQgAnTG89tkwAJkAAJkAAJkAAJ+J4AvFsgUiCHy7333qu8R/AiHuLGqVOnzIQff/yxJEuWTIYMGWLqkLgaIgAMooEOt3X//feLFl7g1WK1zZs3S/369R1EinLlyinPF4g7EIcwFkKCLV68WEqWLClLly6VsmXLWoeJUYZYA48RZ4MXyhNPPGGqt2zZIvDysYokGBueNylSpFDzQwDC/JhXz481urNVq1YJPIsuXbqkOMELBzkmIMpYvYNGjx6thvnyyy9th8M4EMOuXr2q2pF/AR40mD9DhgyC9a9bt061gRX6rlmzRnksWQfctWuX8pSBCKUN3j1lypRRXjm4Fl478Br66aefBG34XahataruziMJkAAJkAAJkAAJkAAJkEAYEqAAE4Y3nVsmARIgARIgARIgARLwD4EPPvhADYxwVB999JGDt8mGDRuUWHLu3DnV5+uvv5a+fftKqlSp1Hm/fv1U2DKcFClSRBAGDDZ8+HAlDKgTyw+EuoIHiBY/EE4LYz766KOWXqI8YOABcvDgQTl79qy8/PLLAq8aiBGuDOHIYDVr1pTWrVtL9uzZlaAA7xXttQMPEniE6PkhNGH+OnXqOAxr9UDB3rt06SLr1693O/+HH36oxmjWrJlArMqTJ48ZE0IHRCctxMC7BmHatEilO0IEa9eunRFfIB6hL7xqrAahpHnz5oI8NxgbYdacBR2sWYsvuXLlUu0NGza0DqPuFzjDO+fixYuKDQSje+65x6EfT0iABEiABEiABEiABEiABMKHAHPAhM+95k5JgARIgARIgARIgAQS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ffrpp2aGDh06SOrUqc25Pwrvv/++ZM6cWQ194MABef755+XatWsxpjp//ry0atXKhHGrXbu2FChQwPQbMmSIpE+fXp2fOXNGmjRpIq7yykyaNEkg9mjDnPA6opEACZAACZAACZAACZAACYQngWThuW3uOqEJvNHlRXm63cteTYu+NBIgARIgARIggeAhwPBjiXOv8uXLp5LC9+nTRy1gyZIlytuiU6dOUq5cOcmZM6dAePj2229l2bJlZpFVqlSR3r17m/O4FvLmzSu9evUyY2GOEiVKqBwqZcuWFXjJHDx4UM2P0F7aKlWqJHrNus4fR4gvffv2la5du6rhIY5s3bpVebSUL19e1W3ZskWGDRsmEFZgYDZx4kRV1j9y5Mgh/fr1k9dff11VwVOmVKlSAs4YB95Ihw8flu+//17mz5+vL5PSpUsLvI5oJEACJEACJEACJEACJEAC4UuAAkz43vsE3XmViuWlcoWy8sumrW7nRR/0pZEACZAACZAACQQHgciInJI/Mk9wLDYEVwkhBflGunfvrkKAIfTVgAEDXO60TJkysmDBAkmTJo3LPrFpgACTNWtWNf/ly5fl3LlzbueHcLFw4UKfze9pra+88oqkTJlS3njjDcVn165dSrSyuy579uwyY8YMJcI4t7/22mvKkwUiDMKo4TN48GDnbua8SJEiAkEMIcxoJEACJEACJEACJEACJEAC4UuAIcjC994n+M698Wzxpk+CL5wTkgAJkAAJkAAJuCTQ9tlmUrFcKZftbPAvASSI79y5s+zYsUMaNGgg6dKls52wYMGCMm7cOJV7JVOmTLZ94loJTxDM37BhQ7fzf/PNN7J582YTFiyu88X2uhdffFG2bdsmjz32mAmZZh0jefLkSqBBbpqqVatamxzKCCe2c+dOFerNlbASGRkpX375pZoPnjM0EiABEiABEiABEiABEiCB8CaQJCrawhsBd5+QBJDfxZUXDLxfpo0blZDL4VwkQAIkQAIkQAJ+INCr/3A16oDe3fwwOod0RwD/aX/06FElFCCsFsKUIZ8JwmQhMX1C2JEjR8z8CFMG8Sch53e3x7t378q+fftk+/btAlZFixaVwoULS4oUKdxdZtuGPDAQZJD3BvsE5zx58sg999xj2z9YK8P97znc9x+sv7dcNwnYEeDfsx0V1pEACZAACfibAEOQ+Zswx3cg4C4XDL1fHFDxhARIgARIgARIgARiTQAeMRAD8EksS+z53e0bItQDDzygPu76edMGsQUfGgmQAAmQAAmQAAmQAAmQAAm4IkABxhUZ1vuFgKtcMMz94hfcHJQESIAESIAEYkXgTrR3wNRZC+T33fui82akkHatmkvunAyjFCuI7EwCJEACJEACJEACJEACJEACJEAC/yOQMHEIiJsELATsPF3s6iyXsEgCJEACJEACJOBnAhRf/AyYw5MACZAACZAACZAACZAACZAACYQdAXrAhN0tT/wNO3vB0Psl8e8JV5A4BEaPnyJH/zqeOJNzVhIgAZ8TyJsnl3R8/hmfj5sQA1J8SQjKnIMESIAESIAESIAESIAESIAESCDcCNADJtzueIDs1+rxYi0HyPK4DBJIEAIUXxIEMychgQQjcOTP4BRUKb4k2K8IJyIBEiABEiABEiABEiABEiABEggzAgHvtoaq2gAAQABJREFUAcNviIfub2SObNnV5uYv+UnwoYUHgWD+hri/7tCA3t38NTTHJQESSCACvfoPT6CZfDsNxRff8uRoJEACJEACJEACJEACJEACJEACJGAlEPACDL8hbr1doVUuVaJ4aG2Iu/GKQLB+Q9yrzbETCZAACQQRAYovQXSzuFQSIAESIAESIAESIAESIAESIIGgJBDwAoymym+IaxI8kkDwEgjWb4gHL3GunARIgATsCVB8sefCWhIgARIgARIgARIgARIgARIgARLwJQHmgPElTY5FAiRAAiRAAiRAAgFOgOJLgN8gLo8ESIAESIAESIAESIAESIAESCBkCFCACZlbyY2QAAmQAAmQAAmQgHsCFF/c82ErCZAACZAACZAACZAACZAACZAACfiSAAUYX9LkWCRAAiRAAiRAAiQQoAQovgTojeGySIAESIAESIAESIAESIAESIAEQpYABZiQvbXcGAmQAAmQAAmQAAn8S4DiC38TSIAESIAESIAESIAESIAESIAESCDhCVCASXjmnJEESIAESIAESIAEEowAxZcEQ82JSIAESIAESIAESIAESIAESIAESMCBAAUYBxw8IQESIAESIAESIIHQIUDxJXTuJXdCAiRAAiRAAiRAAiRAAiRAAiQQfAQowATfPeOKSYAESIAESIAESMAjAYovHhGxAwmQAAmQAAmQAAmQAAmQAAmQAAn4lQAFGL/i5eAk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oGnjO9/5jmibWkbb+cYbb5SbbrrJXEv/+N73vifnnnuus+3vyv333y8PPfSQOS0tLc38XR8be/ifWuecc440NjZ6NPmTn/xETjvtNI99vmw0NDTI+eef7/Ec9edPfw4pCCCAAAIIIIAAAgggEHwCIRuA2b5rj/T0iEwtmCgZ6WnBJ0uPEBgFgVkzp0lFVbVoALK4tNwEZEbhMjSJwIgJVFdXy+rVq/u0995778lJJ53UZ/9gO+6+++4+7emXiqFSdu3a5fR/KPff+z4LCwud9hYvXtz7MNsIhI1AZWWl87OuN6Xzog2nVFVVebTX1dXlc3PHH3+8bNmyRerr6805991337ACMP/zP/8j+neDln/7t39zgi+6vWbNGtEv3N1FgzVDCcD85S9/kVWrVrmbkmnTpnls97fx6quvmkMFBQWyYMECp9r69eulqKjIbOvf9RoM1yCKP+WDDz6Q3bt3m1PKysqcU0fbedmyZbJt2zapra0119TnOJwAzB133GHa08auvvpqsYMvuq3Psfd/qx555JEhBWC8PUcNflEQQAABBBBAAAEEEEAgOAVCMm2ktLxS6uobJSE+3hp6rCA4ZekVAqMgEGv9RuycmdNNy7sL/3975wEfR3W97eMq9957B2ODsU2vpn9AQocQav6EFgid0FvoJIQSAqEkhJAQAkkooYVOIHQwxTZuuMm9W+6SbPPNe+U7jOTd1a60K81Kz/FvvbMzd+7c+8zuaOa+95wzy0o3bMjBUagSArkn8PTTT2d8EA3yvf/++xnvxw4QgAAEskmgRRD69sc//nFYpbzavBgTrkxzQaKFF1+0y+mnn17pnhqA31CFv/9///vfK607UYHvghlPr732mtskj5lUdu2119qkSZNSFUl7W645Nwty65144olhe15++eVQjAlXprnwySefhOKLdknnPD733HNWWlqa5hG+L1bV8/h9DSxBAAIQgAAEIAABCEAAAjVJIO8EGM1QnDJthmM0aEDfcrPLahIcx4JAbRHo1rWztW/bxkqCh/bpMwtrqxkcFwLVIqCwWRrUy8SeeuqpjPfJpP58LNuxY0fr37+/e2kZgwAEaobAT3/60/BA69evD0MBhivTXJAXhLcRI0aYvD4qM+VgefPNNysrVm67PIjeeuutcuvS/TB27FjT/jKF0kplYvGTn/zEMvEoSlVfrjnLU8Vb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)"]},{"cell_type":"markdown","metadata":{"id":"9z1C02QkaPUr"},"source":["Podemos ver o resultado da inferência de tipo usando o método `.inspect_types()`, que imprime uma versão anotada do código-fonte:\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"jNmDDo-YNLYN"},"outputs":[],"source":["hypot.inspect_types()"]},{"cell_type":"markdown","metadata":{"id":"2teFIxUfNLYO"},"source":["Observe que os nomes de tipo de Numba tendem a se assemelhar aos [tipos de dados do Numpy](https://docs.scipy.org/doc/numpy-1.13.0/user/basics.types.html), então um Python `float` é um `float64` (também chamado de \"precisão dupla\" em outras linguagens). Observar os tipos de dados às vezes pode ser importante no código da GPU porque o desempenho dos cálculos `float32` e `float64` pode (dependendo da GPU) ser muito diferente em dispositivos CUDA. Se o seu algoritmo pode obter resultados corretos usando `float32`, então você provavelmente deve usar esse tipo de dados, porque a conversão para `float64` pode, dependendo do tipo de GPU, diminuir drasticamente a função.\n"]},{"cell_type":"markdown","metadata":{"id":"Mr3x0h5wNLYP"},"source":["## Objeto e Modo \"nopython\" \n","\n","Numba não pode compilar todo o código Python. Algumas funções não têm uma tradução Numba, e alguns tipos de tipos Python não podem ser compilados de forma eficiente (ainda). Por exemplo, Numba não suporta dicionários (no momento da redação deste artigo). Por conta disso, o Numba retornará um modo chamado **modo de objeto**, que não faz especialização de tipo. O modo de objeto existe para habilitar outras funcionalidades do Numba, mas em muitos casos, você deseja que o Numba informe se o tipo da inferência irá falhar. Você pode forçar o **nopython mode** (o outro modo de compilação).\n","Aqui vamos tentar compilar algum código Python que o Numba ainda não sabe compilar, passando o argumento `nopython` para o decorador:\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":521},"executionInfo":{"elapsed":709,"status":"error","timestamp":1667863701720,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"v_jxq_OuNLYQ","outputId":"2a90ff8c-b8bf-4170-85e2-c2087797ef86"},"outputs":[],"source":["@jit(nopython=True)\n","def cannot_compile(x):\n"," return x['key']\n","\n","cannot_compile(dict(key='value'))"]},{"cell_type":"markdown","metadata":{"id":"V9_ydZ-1NLYQ"},"source":["Agora, obtemos uma exceção quando o Numba tenta compilar a função e, se você rolar para baixo até o final da saída da exceção, verá um erro que descreve o erro da célula acima:\n","```\n","- argument 0: cannot determine Numba type of \n","```\n","**Usar o modo `nopython` é a maneira recomendada e prática recomendada de usar `jit`, pois leva ao melhor desempenho.**\n","\n","Numba fornece outro decorador `njit` que é um alias para `jit(nopython=True)`:"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":521},"executionInfo":{"elapsed":14,"status":"error","timestamp":1667863741713,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"gtcTDo4SNLYR","outputId":"7cde8046-b647-423c-f360-495270acd218"},"outputs":[],"source":["from numba import njit\n","\n","@njit\n","def cannot_compile(x):\n"," return x['key']\n"," \n","cannot_compile(dict(key='value'))"]},{"cell_type":"markdown","metadata":{"id":"N3zcxBjVNLYS"},"source":["Acesse a [documentação Numba](https://numba.pydata.org/numba-doc/dev/reference/pysupported.html), para maiores informações sobre funções suportadas em Python."]},{"cell_type":"markdown","metadata":{"id":"x7J0DpJjNLYT"},"source":["## Introdução ao Numba para a GPU com NumPy Universal Functions (ufuncs)\n","\n","Começaremos nossa cobertura da programação de GPU no Numba com como compilar [Funções universais NumPy \\(ou ufuncs\\)](https://docs.scipy.org/doc/numpy-1.15.1/reference/ufuncs.html) para a GPU.\n","\n","A coisa mais importante a saber sobre a programação de GPU quando começamos é que o hardware da GPU é projetado para *paralelismo de dados*. A taxa de transferência máxima é alcançada quando a GPU está computando as mesmas operações em muitos elementos diferentes ao mesmo tempo.\n","\n","As funções universais do NumPy, que executam a mesma operação em todos os elementos em uma matriz NumPy, são naturalmente paralelas aos dados, portanto, são um ajuste natural para a programação da GPU.\n","\n","\n"]},{"cell_type":"markdown","metadata":{"id":"Cdi5OLh3NLYU"},"source":["## Fazendo ufuncs para a GPU\n","\n","O Numba tem a capacidade de criar ufuncs *compilados*, normalmente um processo não tão simples envolvendo código C. Com o Numba você simplesmente implementa uma função escalar a ser executada em todas as entradas, decora-a com `@vectorize`, e o Numba descobrirá as regras de transmissão para você. Para aqueles que estão familiarizados com o `vectorize` do NumPy (https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.vectorize.html), o decorador `vectorize` do Numba será muito familiar."]},{"cell_type":"markdown","metadata":{"id":"4YRxfrbNNLYV"},"source":["Em nosso primeiro exemplo, iremos usar o `@vectorize` decorador para compilar e otimizar a ufunc para **CPU**.\n"]},{"cell_type":"code","execution_count":null,"metadata":{"executionInfo":{"elapsed":4,"status":"ok","timestamp":1667864302777,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"K9dd2VFiNLYX"},"outputs":[],"source":["from numba import vectorize\n","import numpy as np \n","\n","@vectorize\n","def add_ten(num):\n"," return num + 10 # This scalar operation will be performed on each element"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":12,"status":"ok","timestamp":1667864305858,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"L98gDv0ONLYZ","outputId":"1f84824b-f9e0-4ff1-a7d3-78f40327992a"},"outputs":[],"source":["nums = np.arange(10)\n","add_ten(nums) # pass the whole array into the ufunc, it performs the operation on each element"]},{"cell_type":"markdown","metadata":{"id":"VVtQQUc2NLYZ"},"source":["Estamos gerando um ufunc que usa CUDA na GPU com a adição de fornecer uma **assinatura de tipo explícita** e definir o atributo `target`. O argumento de assinatura de tipo descreve quais tipos usar tanto para os argumentos ufuncs quanto para o valor de retorno:\n","``` python\n","'return_value_type(argument1_value_type, argument2_value_type, ...)'\n","```\n","\n","Consulte os documentos do Numba para obter mais informações sobre [tipos disponíveis](https://numba.pydata.org/numba-doc/dev/reference/types.html), bem como para obter informações adicionais sobre [escrever ufuncs com mais de um assinatura](https://numba.pydata.org/numba-doc/dev/user/vectorize.html)\n","\n","Aqui está um exemplo simples de um ufunc que será compilado para um dispositivo GPU habilitado para CUDA. Ele espera dois valores `int64` e retorna também um valor `int64`:"]},{"cell_type":"code","execution_count":null,"metadata":{"executionInfo":{"elapsed":449,"status":"ok","timestamp":1667864310652,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"jyMUGmhjNLYa"},"outputs":[],"source":["@vectorize(['int64(int64, int64)'], target='cuda') # Type signature and target are required for the GPU\n","def add_ufunc(x, y):\n"," return x + y"]},{"cell_type":"markdown","metadata":{"id":"S-hOqw1cdctQ"},"source":["Para quem está familiarizado com o uso de de arrays de diferentes dimensões por meio da Biblioteca NumPy, sabe que é possivel fazer a associação de escalares com arrays. No exemplo a seguir de soma, observe que o valor escalar é propagado por uma técnica conhecida como [*broadcasting*](https://docs.scipy.org/doc/numpy-1.15.0/user/basics.broadcasting.html)."]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":437,"status":"ok","timestamp":1667864987125,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"m-Xr0nwGNLYb","outputId":"01229aba-54ed-45c2-9a77-578eda8e2f89"},"outputs":[],"source":["#Lembrando que arrays de \n","#For more information, check docs for numpy.arange, numpy.ndarray.reshape\n","\n","a = np.array([1, 2, 3, 4])\n","b = np.array([10, 20, 30, 40])\n","\n","np.add(b, 100)"]},{"cell_type":"markdown","metadata":{"id":"Ku3czr8sfGMS"},"source":["A mesma técnica acontece com arrays de tamanhos diferentes, desde que haja compatibilidade. O processo é que o array de menor dimensão é replicado para combinar com a dimensão do array de maior dimensão. Caso deseje maiores informações sobre tal processo, busque por: \n","[`numpy.arange`](https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.arange.html) and [`numpy.ndarray.reshape`](https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.ndarray.reshape.html), pois os mesmos serão amplamente utilizados no curso. \n","\n","\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":727,"status":"ok","timestamp":1667864994596,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"5gi4Xbf1f9Q9","outputId":"48600e5d-6853-40c1-fea8-348c38222a3e"},"outputs":[],"source":["c = np.arange(4*4).reshape((4,4))\n","print('c:', c)\n","\n","np.add(b, c)"]},{"cell_type":"markdown","metadata":{"id":"FFlu6d-9NLYb"},"source":["Para uma chamada de função tão simples, muitas coisas simplesmente aconteceram! Numba apenas automaticamente:\n","\n"," * Compilou um kernel CUDA para executar a operação ufunc em paralelo sobre todos os elementos de entrada.\n"," * Memória GPU alocada para as entradas e saídas.\n"," * Copiou os dados de entrada para a GPU.\n"," * Executou o kernel CUDA (função GPU) com as dimensões corretas do kernel, considerando os tamanhos de entrada.\n"," * Copiou o resultado de volta da GPU para a CPU.\n"," * Devolveu o resultado como um array NumPy no host.\n"," \n","Comparado a uma implementação em C, o acima é notavelmente mais conciso.\n","\n","Você pode estar se perguntando o quão rápido nosso exemplo simples está na GPU? Vamos ver:"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":8822,"status":"ok","timestamp":1667865113006,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"YOYMkHaYNLYc","outputId":"d9de1ddb-e797-455f-83c5-b73343bb6a00"},"outputs":[],"source":["%timeit np.add(b, c) # NumPy on CPU"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":1117,"status":"ok","timestamp":1667865114104,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"wtEKAQdlNLYe","outputId":"86965edf-644b-4f07-da38-2a496e58ba52"},"outputs":[],"source":["%timeit add_ufunc(b, c) # Numba on GPU"]},{"cell_type":"markdown","metadata":{"id":"JtE7fD-tNLYf"},"source":["Wow, a GPU é *muito mais lenta* que a CPU?? Por enquanto, isso é esperado porque usamos (deliberadamente) a GPU de várias maneiras neste exemplo. Como usamos mal a GPU ajudará a esclarecer quais tipos de problemas são adequados para a computação da GPU e quais são melhores para serem executados na CPU:\n","\n"," * **Nossas entradas são muito pequenas**: a GPU obtém desempenho por meio do paralelismo, operando em milhares de valores de uma só vez. Nossas entradas de teste têm apenas 4 e 16 inteiros, respectivamente. Precisamos de uma matriz muito maior para manter a GPU ocupada.\n"," * **Nosso cálculo é muito simples**: enviar um cálculo para a GPU envolve um pouco de sobrecarga em comparação com a chamada de uma função na CPU. Se nosso cálculo não envolver operações matemáticas suficientes (geralmente chamadas de \"intensidade aritmética\"), a GPU passará a maior parte do tempo esperando que os dados se movam.\n"," * **Copiamos os dados do Host para a GPU**: embora em alguns cenários, pagar o custo de copiar dados do host para a GPU possa valer a pena para uma única função, geralmente será preferível executar várias operações GPUs em sequência. Nesses casos, faz sentido enviar dados para a GPU e mantê-los lá até que todo o nosso processamento seja concluído.\n"," * **Nossos tipos de dados são maiores que o necessário**: Nosso exemplo usa `int64` quando provavelmente não precisamos dele. O código escalar usando tipos de dados de 32 e 64 bits executa basicamente a mesma velocidade na CPU e, para tipos inteiros, a diferença pode não ser drástica, mas os tipos de dados de ponto flutuante de 64 bits podem ter um custo de desempenho significativo na GPU, dependendo do tipo de GPU. A aritmética básica em floats de 64 bits pode ser de 2x (Pascal-architecture Tesla) a 24x (Maxwell-architecture GeForce) mais lenta do que floats de 32 bits. Se você estiver usando GPUs mais modernas (Volta, Turing, Ampere), isso pode ser muito menos preocupante. O NumPy é padronizado para tipos de dados de 64 bits ao criar matrizes, por isso é importante definir o [`dtype`](https://docs.scipy.org/doc/numpy-1.14.0/reference/arrays.dtypes.html ) ou use o método [`ndarray.astype()`](https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.ndarray.astype.html) para escolher o tipo de 32 bits quando você precisar deles.\n"," \n"," \n","Dado o exposto, vamos tentar um exemplo que é mais rápido na GPU realizando uma operação com intensidade aritmética muito maior, em uma entrada muito maior e usando um tipo de dados de 32 bits.\n","\n","**Observação:** Nem todo código NumPy funcionará na GPU e, como no exemplo a seguir, precisaremos usar o `pi` e o `exp` da biblioteca `math` em vez do NumPy. Consulte [os documentos do Numba](https://numba.pydata.org/numba-doc/latest/reference/numpysupported.html) para obter uma ampla cobertura do suporte ao NumPy na GPU.\n","\n"]},{"cell_type":"code","execution_count":null,"metadata":{"executionInfo":{"elapsed":444,"status":"ok","timestamp":1667865448625,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"84YCksIqNLYg"},"outputs":[],"source":["import math # Note that for the CUDA target, we need to use the scalar functions from the math module, not NumPy\n","\n","SQRT_2PI = np.float32((2*math.pi)**0.5) # Precompute this constant as a float32. Numba will inline it at compile time.\n","\n","@vectorize(['float32(float32, float32, float32)'], target='cuda')\n","def gaussian_pdf(x, mean, sigma):\n"," '''Compute the value of a Gaussian probability density function at x with given mean and sigma.'''\n"," return math.exp(-0.5 * ((x - mean) / sigma)**2) / (sigma * SQRT_2PI)"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":11,"status":"ok","timestamp":1667865450750,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"8EdT60x1NLYi","outputId":"27d6ddbd-4f87-4998-f228-d5b24b7a775d"},"outputs":[],"source":["import numpy as np\n","# calculamos a Gaussian 1 milhao de vezes!\n","x = np.random.uniform(-3, 3, size=1000000).astype(np.float32)\n","mean = np.float32(0.0)\n","sigma = np.float32(1.0)\n","\n","# Rapido teste em um elemento, para se ter certeza do funcionamento.\n","gaussian_pdf(x[0], 0.0, 1.0)"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":2524,"status":"ok","timestamp":1667865455582,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"8Wdi9zrtNLYj","outputId":"af636fbb-bd2c-49b0-9eec-4aad7d826069"},"outputs":[],"source":["import scipy.stats # for definition of gaussian distribution, so we can compare CPU to GPU time\n","norm_pdf = scipy.stats.norm\n","%timeit norm_pdf.pdf(x, loc=mean, scale=sigma)"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":5140,"status":"ok","timestamp":1667865460713,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"6Lvv4j1oNLYj","outputId":"73668676-3344-45ec-bbe1-451ae988e37b"},"outputs":[],"source":["%timeit gaussian_pdf(x, mean, sigma)"]},{"cell_type":"markdown","metadata":{"id":"vLRT9z7kNLYk"},"source":["Podemos notar uma grande melhoria, ainda incluindo acarga de cópia para todos os dados do host para GPU. Ufuncs que usam funções especiais (`exp`, `sin`, `cos`, etc) em data sets largos rodam especialmente bem na GPU. \n","\n","Para completar nossa comparação, vamos definir e cronometrar nossa função `gaussian_pdf` quando otimizada pelo Numba para **CPU**: "]},{"cell_type":"code","execution_count":null,"metadata":{"executionInfo":{"elapsed":421,"status":"ok","timestamp":1667865474417,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"CXKISzUXNLYk"},"outputs":[],"source":["@vectorize\n","def cpu_gaussian_pdf(x, mean, sigma):\n"," '''Compute the value of a Gaussian probability density function at x with given mean and sigma.'''\n"," return math.exp(-0.5 * ((x - mean) / sigma)**2) / (sigma * SQRT_2PI)"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":2104,"status":"ok","timestamp":1667865477136,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"Y0Xtjhq3NLYl","outputId":"9a07ee66-4dcf-448c-edd1-e01a160df011"},"outputs":[],"source":["%timeit cpu_gaussian_pdf(x, mean, sigma)"]},{"cell_type":"markdown","metadata":{"id":"P9PdB025NLYl"},"source":["Oque resulta em menor tempo de execução que a versão não compilada em CPU, porém muito mais lento que uma aceleração em GPU."]},{"cell_type":"markdown","metadata":{"id":"eLY5RDvaNLYm"},"source":["## Funções do dispositivo CUDA\n","\n","Ufuncs são realmente fantásticos quando se deseja realizar operações de elementos, o que é uma tarefa muito comum. No entanto, existem várias funções que não se encaixam nesta descrição. Para compilar funções para a GPU que **não** são funções vetorizadas de elementos, usamos `numba.cuda.jit`. Na próxima seção deste curso trabalhamos extensivamente com `numba.cuda.jit`, mas por enquanto, vamos demonstrar como usá-lo para decorar uma função auxiliar, a ser utilizada por um ufunc acelerado por GPU, para que nao seja necessário remontar toda a lógica em uma única definição de ufunc.\n","\n","Observe que o `polar_to_cartesian` abaixo não requer uma assinatura de tipo, e também, que são passados ​​dois valores escalares, ao contrário dos ufuncs vetorizados que estamos usando (como o `polar_distance` abaixo) que esperam arrays NumPy como argumentos.\n","\n","O argumento `device=True` indica que a função decorada **somente** pode ser chamada de uma função em execução na GPU, e não do código host da CPU:"]},{"cell_type":"code","execution_count":null,"metadata":{"executionInfo":{"elapsed":686,"status":"ok","timestamp":1667865758736,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"B_em6dWXNLYm"},"outputs":[],"source":["from numba import cuda\n","\n","@cuda.jit(device=True) #chamada de device\n","def polar_to_cartesian(rho, theta):\n"," x = rho * math.cos(theta)\n"," y = rho * math.sin(theta)\n"," return x, y\n","\n","#declaração de vetorização - aqui ocorre a paralelização\n","@vectorize(['float32(float32, float32, float32, float32)'], target='cuda')\n","def polar_distance(rho1, theta1, rho2, theta2):\n"," x1, y1 = polar_to_cartesian(rho1, theta1) # We can use device functions inside our GPU ufuncs\n"," x2, y2 = polar_to_cartesian(rho2, theta2)\n"," \n"," return ((x1 - x2)**2 + (y1 - y2)**2)**0.5"]},{"cell_type":"code","execution_count":null,"metadata":{"executionInfo":{"elapsed":510,"status":"ok","timestamp":1667865783776,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"iPiTs2JXNLYm"},"outputs":[],"source":["n = 1000000\n","rho1 = np.random.uniform(0.5, 1.5, size=n).astype(np.float32)\n","theta1 = np.random.uniform(-np.pi, np.pi, size=n).astype(np.float32)\n","rho2 = np.random.uniform(0.5, 1.5, size=n).astype(np.float32)\n","theta2 = np.random.uniform(-np.pi, np.pi, size=n).astype(np.float32)\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":8,"status":"ok","timestamp":1667865784930,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"tx7CfdcgNLYn","outputId":"1ef3eb0b-746f-4a1d-a6c2-5b8318fe6c5b"},"outputs":[],"source":["polar_distance(rho1, theta1, rho2, theta2)\n"]},{"cell_type":"markdown","metadata":{"id":"ndUsgCt_NLYo"},"source":["Observe que o compilador CUDA faz uso de um comando frequente para usuários de C++, uma analogia que pode-se fazer é que o compilador faz uso do `inline function`. Por conta disso, geralmente não há sobrecargas para chamadas de função (a serem executadas no device). Da mesma forma. Da mesma forma, a \"tupla\" retornada por `polar_to_cartesian` não é realmente criada como um objeto Python, mas representada temporariamente como uma estrutura, que é então otimizada pelo compilador."]},{"cell_type":"markdown","metadata":{"id":"yULs3TnUNLYo"},"source":["## Python permitido na GPU\n","\n","Comparado ao Numba na CPU (que já é limitado), o Numba na GPU tem mais limitações. O Python suportado inclui:\n","\n","* `if`/`elif`/`else`\n","* loops `while` e `for`\n","* Operadores matemáticos básicos\n","* Funções selecionadas dos módulos `math` e `cmath`\n","* Tuplas\n","\n","Consulte [o manual do Numba](http://numba.pydata.org/numba-doc/latest/cuda/cudapysupported.html) para obter mais detalhes.\n","\n"]},{"cell_type":"markdown","metadata":{"id":"XELHxv2qNLYp"},"source":["### Exercício: Aceleração via GPU de uma função\n","\n","Vamos GPU acelerar uma função de \"supressão zero\". Uma operação comum ao trabalhar com formas de onda é forçar todos os valores de amostra abaixo de uma certa magnitude absoluta a serem zero, como forma de eliminar o ruído de baixa amplitude. Vamos fazer alguns dados de exemplo:\n","\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":282},"executionInfo":{"elapsed":1983,"status":"ok","timestamp":1667866364176,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"0Ywe1rd4NLYp","outputId":"c487826d-f9dd-492a-b3cf-ecab2266df90"},"outputs":[],"source":["# This allows us to plot right here in the notebook\n","%matplotlib inline\n","\n","# Hacking up a noisy pulse train\n","from matplotlib import pyplot as plt\n","\n","@vectorize(['float32(float32, float32)'])\n","def zero_suppress(waveform_value, threshold):\n"," if waveform_value < threshold:\n"," result = 0\n"," else:\n"," result = waveform_value\n"," return result\n","\n","\n","n = 100000\n","noise = np.random.normal(size=n) * 3\n","pulses = np.maximum(np.sin(np.arange(n) / (n / 23)) - 0.3, 0.0)\n","waveform = ((pulses * 300) + noise).astype(np.int16)\n","plt.plot(waveform)"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":282},"executionInfo":{"elapsed":709,"status":"ok","timestamp":1667866364868,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"CgmukxH-NLYq","outputId":"3edb1d93-5613-4af2-8695-fe374cb8ca87"},"outputs":[],"source":["# This will throw an error until you successfully vectorize the `zero_suppress` function above.\n","# The noise on the baseline should disappear when zero_suppress is implemented\n","plt.plot(zero_suppress(waveform, 15))"]},{"cell_type":"markdown","metadata":{"id":"Pj53bY7rNLYr"},"source":["## Gerenciando a memória da GPU\n","\n","Até agora, usamos matrizes NumPy na CPU como entradas e saídas para nossas funções de GPU. Por conveniência, o Numba transferiu automaticamente esses dados para a GPU para que possam ser operados pela GPU. Com esta transferência de dados implícita, o Numba, agindo de forma conservadora, transferirá automaticamente os dados de volta para a CPU após o processamento. Como você pode imaginar, esta é uma operação muito demorada.\n","\n","O [Guia de práticas recomendadas do CUDA](https://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html) indica:\n","\n","> **Alta Prioridade**: Minimize a transferência de dados entre o host e o dispositivo, mesmo que isso signifique executar alguns kernels no dispositivo que não apresentam ganhos de desempenho quando comparados com executá-los na CPU do host.\n","\n","Com isso em mente, devemos considerar como evitar essa transferência automática de dados de volta ao host para que possamos realizar trabalho adicional nos dados, pagando apenas o preço de copiá-los de volta para o host quando estivermos realmente prontos.\n","\n","A maneira de fazer isso é criar **CUDA Device Arrays** e passá-los para nossas funções de GPU. As matrizes de dispositivos não serão transferidas automaticamente de volta para o host após o processamento e podem ser reutilizadas conforme desejarmos no dispositivo antes de finalmente, e somente se necessário, enviá-las, ou partes delas, de volta ao host.\n","\n","Para demonstrar, vamos criar nosso exemplo de adição ufunc novamente:"]},{"cell_type":"code","execution_count":null,"metadata":{"executionInfo":{"elapsed":6,"status":"ok","timestamp":1667866411425,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"d86IVA0UVlmt"},"outputs":[],"source":["@vectorize(['float32(float32, float32)'], target='cuda')\n","def add_ufunc(x, y):\n"," return x + y"]},{"cell_type":"code","execution_count":null,"metadata":{"executionInfo":{"elapsed":430,"status":"ok","timestamp":1667866541712,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"DuimXzFRl4B9"},"outputs":[],"source":["n = 100000\n","x = np.arange(n).astype(np.float32)\n","y = 2 * x"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":6,"status":"ok","timestamp":1667866541713,"user":{"displayName":"Gustavo de Macena Barreto","userId":"13320875132263125020"},"user_tz":180},"id":"NyUzRJBhVoJO","outputId":"32e35ca7-b10c-4e7c-a33b-9455dfcee210"},"outputs":[],"source":["%timeit add_ufunc(x, y) # Baseline performance with host arrays"]},{"cell_type":"markdown","metadata":{"id":"xEleGWzeP8hI"},"source":["O módulo numba.cuda inclui uma função que copiará os dados do host para a GPU e retornará uma matriz de dispositivos CUDA. Observe que abaixo, quando tentamos imprimir o conteúdo do array de dispositivos, obtemos apenas informações sobre o array, e não seu conteúdo real. Isso ocorre porque os dados estão no dispositivo e precisaríamos transferi-los de volta para o host para imprimir seus valores, o que mostraremos como fazer mais adiante. \n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":262,"status":"ok","timestamp":1667778623321,"user":{"displayName":"","userId":""},"user_tz":180},"id":"tcFCCbBmWg1V","outputId":"e2f110b4-119d-4c13-878f-fbb8cd1e7ba4"},"outputs":[],"source":["from numba import cuda\n","\n","x_device = cuda.to_device(x)\n","y_device = cuda.to_device(y)\n","\n","print(x_device)\n","print(x_device.shape)\n","print(x_device.dtype)"]},{"cell_type":"markdown","metadata":{"id":"QFX--zycWuzs"},"source":["Os arrays de dispositivos podem ser passados ​​para funções CUDA assim como os arrays NumPy, mas sem a sobrecarga de cópia:\n","\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":1290,"status":"ok","timestamp":1667778693020,"user":{"displayName":"","userId":""},"user_tz":180},"id":"-zQAM2j_WyiD","outputId":"e81ccfca-79a4-4f96-e569-0ae5d50261fa"},"outputs":[],"source":["%timeit add_ufunc(x_device, y_device)"]},{"cell_type":"markdown","metadata":{"id":"T_Fxfn7XXbbp"},"source":["Como x_device e y_device já estão no dispositivo, esse benchmark é muito mais rápido.\n","\n","No entanto, ainda estamos alocando um array de dispositivos para a saída do ufunc e copiando-o de volta para o host, embora na célula acima não estejamos realmente atribuindo o array a uma variável. Para evitar isso, podemos criar o array de saída com a função numba.cuda.device_array():"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"jCCNqRcEXgM2"},"outputs":[],"source":["out_device = cuda.device_array(shape=(n,), dtype=np.float32) # does not initialize the contents, like np.empty()"]},{"cell_type":"markdown","metadata":{"id":"_-paOFHbXo69"},"source":["E então podemos usar um argumento de palavra-chave especial para o ufunc para especificar o buffer de saída:"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":7373,"status":"ok","timestamp":1667779416811,"user":{"displayName":"","userId":""},"user_tz":180},"id":"e6EvnlCsXsLd","outputId":"684c9f98-427f-4066-9858-c25d8ee3f849"},"outputs":[],"source":["%timeit add_ufunc(x_device, y_device, out=out_device)"]},{"cell_type":"markdown","metadata":{"id":"QNQ1qImSX-Jm"},"source":["Você pode estar pensando que não estamos comparando maçãs com maçãs aqui, pois não estamos comparando as chamadas `to_device` ao usar os arrays de dispositivos, embora as transferências de dados implícitas estejam sendo contadas para o benchmarking quando usamos arrays de host `a` e ` b`, e você estaria correto. Claro que nossa função `add_func` não é particularmente adequada para a GPU como discutido anteriormente. O acima foi apenas destinado a demonstrar como as transferências podem ser eliminadas.\n","\n","Certifique-se de comparar suas transferências de dados ao explorar se uma viagem à GPU vale ou não a pena.\n","\n","Além disso, o Numba fornece métodos adicionais para gerenciar a memória do dispositivo e a transferência de dados, confira [os documentos](https://numba.pydata.org/numba-doc/dev/cuda/memory.html) para obter detalhes completos."]},{"cell_type":"markdown","metadata":{"id":"utJZ-825Y2Qq"},"source":["### Exercício: Otimize o Movimento da Memória\n","\n","Dado estes ufuncs:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"qechwpM5Y06m"},"outputs":[],"source":["import math\n","\n","@vectorize(['float32(float32, float32, float32)'], target='cuda')\n","def make_pulses(i, period, amplitude):\n"," return max(math.sin(i / period) - 0.3, 0.0) * amplitude\n","\n","n = 100000\n","noise = (np.random.normal(size=n) * 3).astype(np.float32)\n","t = np.arange(n, dtype=np.float32)\n","period = n / 23"]},{"cell_type":"markdown","metadata":{"id":"X71SSHacY2bK"},"source":["Como está atualmente na célula abaixo, há uma ida e volta de dados desnecessária de volta ao host e depois de volta ao dispositivo entre as chamadas para `make_pulses` e `add_ufunc`.\n","\n","Atualize a célula abaixo para usar alocações de dispositivos para que haja apenas uma cópia para o dispositivo antes da chamada para `make_pulses` e uma cópia de volta para o host após a chamada para `add_ufunc`. "]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"background_save":true,"base_uri":"https://localhost:8080/","height":282},"executionInfo":{"elapsed":827,"status":"ok","timestamp":1667781101201,"user":{"displayName":"","userId":""},"user_tz":180},"id":"REqZ0IPVZX44","outputId":"a0118c1f-77e0-4a1e-ab15-2bdac3809fd2"},"outputs":[],"source":["%matplotlib inline\n","from matplotlib import pyplot as plt\n","\n","pulses = make_pulses(t, period, 100.0)\n","waveform = np.add(pulses, noise)\n","\n","plt.plot(waveform)"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":402},"executionInfo":{"elapsed":271,"status":"error","timestamp":1667781104649,"user":{"displayName":"","userId":""},"user_tz":180},"id":"XJM36pH6ZYPh","outputId":"a0a70cf3-4d9a-4495-8f01-173a107b419c"},"outputs":[],"source":["n = 100000\n","noise = (np.random.normal(size=n) * 3).astype(np.float32)\n","t = np.arange(n, dtype=np.float32)\n","period = n / 23\n","\n","d_noise = cuda.to_device(noise)\n","d_t = cuda.to_device(t)\n","d_pulses = cuda.device_array(shape=(n,), dtype=np.float32)\n","\n","make_pulses(d_t, period, 100.0, out=d_pulses)\n","waveform = add_ufunc(d_pulses, d_noise)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"zCmaA1fSZYSM"},"outputs":[],"source":[]},{"cell_type":"markdown","metadata":{"id":"sTce41AoYIqp"},"source":["## Referências\n","\n","- https://courses.nvidia.com/courses/course-v1:DLI+C-AC-01+V1/course/\n","- https://courses.nvidia.com/courses/course-v1:DLI+C-AC-02+V1/course/\n","- https://developer.nvidia.com/blog/how-query-device-properties-and-handle-errors-cuda-cc/"]}],"metadata":{"accelerator":"GPU","colab":{"collapsed_sections":[],"provenance":[{"file_id":"https://github.com/MBGustav/pp/blob/master/code/CUDA/CUDA.ipynb","timestamp":1667781130031}]},"gpuClass":"standard","kernelspec":{"display_name":"Python 3.7.6 ('base')","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.7.6"},"vscode":{"interpreter":{"hash":"7fb4e08d40c87d182d3eb80acd71ade9835941d2f88b8731e54a532e624040ce"}}},"nbformat":4,"nbformat_minor":0}