Skip to content

Afonso017/OpenCL

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Repositório de Pesquisa e Documentação OpenCL

Visão Geral

Este repositório contém materiais de pesquisa, exemplos de código e recursos de documentação da biblioteca OpenCL (Open Computing Language).
Todos os documentos de referências usados nesse repositório podem ser encontrados aqui.

Pré-requisitos

Para trabalhar com os materiais deste repositório é necessário ter:

Estrutura do repositório

  • /examples - Exemplos de código demonstrando conceitos e recursos do OpenCL
  • /linux - Projeto com VS Code + CMake
  • /windows - Projeto com Visual Studio
  • /images - Imagens usadas neste README

Os códigos de exemplo estão implementados usando a api de abstração do OpenCL usando classes e recursos modernos do C++, consulte OpenCL C++ Wrapper API para mais detalhes.

Primeiros Passos (Windows)

Clone este repositório:

git clone https://github.com/Afonso017/OpenCL.git
cd OpenCL

O OpenCL SDK já está instalado no diretório /windows/OpenCL/OpenCL_SDK/bin e o projeto do Visual Studio já está configurado para usar o OpenCL. Após clonar o repositório, entre na pasta do OpenCL SDK e execute o comando clinfo para saber se o OpenCL está configurado corretamente no seu sistema:

cd windows\OpenCL\OpenCL_SDK\bin
clinfo

Exemplo de saída do comando clinfo:

saída do comando clinfo

O clinfo lista e exibe informações sobre plataformas e dispositivos disponíveis, normalmente uma única plataforma e um único dispositivo do tipo GPU em computadores pessoais, e se você instalou o Intel OpenCL Runtime, sua CPU também será listada. Para executar a ferramenta clinfo a qualquer momento, adicione a pasta bin ao PATH do sistema.

Caso a ferramenta não encontre seu dispositivo, provavelmente é algum problema com o driver de vídeo, tente reinstalá-lo.

Rodando programas OpenCL com o Visual Studio

Abrindo o arquivo da solução \windows\OpenCL\OpenCL.sln é possível ver os arquivos de exemplo do diretório \examples pois a estrutura do repositório foi construída visando a compatibilidade entre os sistemas Windows e Linux usando o mesmo código-fonte.

visual studio

Não é possível executar um projeto no Visual Studio com mais de um arquivo com o método main() definido, então cada exemplo possui uma macro MAIN que permite habilitar o código para ser executado.
Descomente a linha //#define MAIN do arquivo hello.cpp e rode o projeto clicando no botão verde ou com o atalho Ctrl + F5 para executar seu primeiro "Hello, World!" no OpenCL.
Para executar outros exemplos, comente a linha #define MAIN do arquivo atual e descomente a do outro arquivo.

É preciso copiar os arquivos .cl para o diretório do projeto para que o programa consiga encontrá-los, eles estão configurados para serem copiados automaticamente durante o build do projeto.
É possível configurar clicando com o botão direito no arquivo .cl, indo em Properties e definindo Item Type para Copy File, depois clique em Aplicar.

propriedades de .cl

Vai aparecer outra propriedade para configurar o caminho do arquivo e o diretório de destino. Defina Destination Directories para $(ProjectDir) e Content Root Folder para ../../examples que é a pasta onde estão os arquivos .cl.

configurando copy file

O que é OpenCL?

Segundo a documentação oficial da Khronos Group, OpenCL é um padrão aberto para programação paralela de alto desempenho para sistemas heterogêneos, incluindo CPUs multicore, GPUs many-core, DSPs e outros processadores. O OpenCL torna possível a escrita de código multi-plataforma para tais dispositivos, sem a necessidade do uso de linguagens e ferramentas específicas de fabricante. Com foco no paralelismo e programação de alto desempenho, o OpenCL baseia-se na escrita de funções que são executadas em múltiplas instâncias simultâneas, chamadas de Kernel. Este repositório visa introduzir conceitos-chave e explorar a arquitetura definida no padrão OpenCL.


Fonte: Programação em OpenCL: Uma introdução prática

O OpenCL funciona em várias camadas de abstração, a camada de mais alto nível é a camada de aplicação, onde é executado o código do host que envia tarefas para o dispositivo através de uma fila de comandos. Enquanto a camada de aplicação pode ser escrita tanto em C como C++, o código dos kernels é escrito em uma variante da linguagem ISO C99. Esses kernels são compilados em tempo de execução e podem ser definidos em um arquivo separado ou simplesmente em uma string dentro do programa.

Onde OpenCL é usado?

O OpenCL é usado principalmente em áreas que demandam alto desempenho. Na computação científica, ele acelera simulações em física, química e modelagem climática, enquanto no processamento de imagens, otimiza algoritmos de visão computacional e análise médica. Também é utilizado em machine learning para inferência em redes neurais, em jogos para física e pós-processamento gráfico, e em finanças para trading de alta frequência. Além disso, o OpenCL é relevante em telecomunicações (processamento de sinais 5G), sistemas embarcados (IoT e FPGAs) e até na mineração de criptomoedas, como no Ethereum.

Uma das principais vantagens do OpenCL em relação ao CUDA é sua portabilidade multiplataforma, funcionando em hardware da AMD, Intel, ARM e outros, sem depender de fabricante específico. Isso o torna ideal para projetos que exigem flexibilidade ou operam em ambientes heterogêneos, embora o CUDA ainda domine em aplicações de deep learning dedicadas a GPUs NVIDIA. Sua capacidade de aproveitar diferentes tipos de processadores faz do OpenCL uma ferramenta valiosa para otimização de desempenho em diversos cenários.

Modelo de Plataforma OpenCL


Fonte: Programação em OpenCL: Uma introdução prática

O modelo de plataforma OpenCL é baseado em uma arquitetura hierárquica que organiza os componentes de hardware e software em diferentes níveis. Ele é composto pelos seguintes elementos principais:

  1. Host/Hospedeiro: O host é o dispositivo principal que controla a execução dos programas OpenCL. Geralmente, é a CPU do sistema, responsável por enviar comandos e gerenciar os dispositivos.

  2. Plataformas: As plataformas representam os fornecedores dos componentes de hardware que suportam implementações do OpenCL. Intel, AMD e NVIDIA, por exemplo.

  3. Dispositivos: São os componentes de hardware físico que executam os kernels OpenCL. Podem incluir GPUs, CPUs adicionais, FPGAs, DSPs ou outros processadores.

  4. Unidades de Computação (Compute Units): Cada dispositivo é dividido em unidades de computação, que são responsáveis por executar as tarefas em paralelo.

  5. Elementos de Processamento (Processing Elements): Dentro de cada unidade de computação, existem elementos de processamento que executam as instruções individuais de um kernel.

O modelo de plataforma também define como os programas OpenCL interagem com esses componentes. Ele utiliza um modelo de execução baseado em filas de comandos, onde o host envia tarefas para os dispositivos por meio de uma API padronizada. Essas tarefas incluem a execução de kernels, transferência de dados entre o host e os dispositivos, e sincronização.

Essa arquitetura permite que o OpenCL seja altamente flexível e escalável, suportando uma ampla variedade de dispositivos heterogêneos em um mesmo programa.

Veja o exemplo /examples/infrastructure.cpp que mostra como selecionar um ou mais dispositivos e plataformas do sistema, bem como exibir algumas informações.

Modelo de Execução


Fonte: Khronos OpenCL-Guide Github Repository

O Runtime OpenCL define como o código do kernel é distribuído pela arquitetura do dispositivo, ele permite que o programador controle a organização dos kernels em work-groups.

Ele também gerencia para quais Elementos de Processamento (Processing Elements) cada instância do kernel será mapeado.

As instâncias do kernel são chamadas de work-items, cada work-item é executado por um Elemento de Processamento distinto, e um grupo de work-items, chamados de work-group, é mapeado à uma Unidade de Computação (Computing Unit) que possui uma memória local compartilhada entre os work-items de um mesmo work-group.

O programador define variáveis OpenCL como globalSize e localSize do tipo NDRange, o globalSize é a quantidade total de instâncias do kernel (work-items) que serão executados, e o localSize é o tamanho de um work-group, ou seja, quantos work-items formam um work-group.

É possível definir globalSize e localSize em até 3 dimensões, o exemplo da imagem usa NDRanges bidimensionais, onde o localSize é 8x8 = 64 work-items por work-group e o globalSize é 4x64 = 256 work-items no total. Cada um dos work-groups será mapeado a uma Unidade de Computação e cada um dos work-items será mapeado a um Elemento de Processamento de sua Unidade de Computação correspondente.

Veja o exemplo /examples/kernel.cpp que mostra como a execução de um kernel se comporta com valores diferentes para globalSize e localSize.

Modelo de Memória


Fonte: Khronos OpenCL-Guide Github Repository

O OpenCL possui hierarquias de memória, relativas às memórias físicas do sistema e dos dispositivos. A memória do host, normalmente, é a memória RAM. A memória constante/global pode ser acessada por qualquer work-item no dispositivo. Cada Unidade de Computação possui uma memória local, compartilhada entre work-items de um mesmo work-group. Cada Elemento de Processamento possui uma memória privada para um work-item correspondente.

O gerenciamento de memória e sincronização entre elas é explícito, o programador precisa criar buffers de comunicação entre CPU e GPU (ou qualquer outro dispositivo) para transferir os dados necessários entre as memórias.

Exemplo de kernel que realiza a soma de dois vetores A e B para um vetor resultante C:

__kernel void vector_sum(
    __global const int* A,  // ponteiro para o primeiro vetor
    __global const int* B,  // ponteiro para o segundo vetor
    __global int* C         // ponteiro para o vetor C
) {
    int id = get_global_id(0); // ID do thread atual, que é o índice do vetor
    C[id] = A[id] + B[id];
}

Perceba que esse kernel realiza a soma apenas para um elemento do array C, mas se definirmos globalSize como o tamanho do array, cada soma será executada, em paralelo, em um work-item específico.

O método get_global_id(0) retorna o índice do work-item correspondente na dimensão 0, podemos chamar esse método apenas com os números 0, 1 e 2, representando as 3 dimensões possíveis de um NDRange.

Veja o exemplo /examples/vector_sum.cpp que mostra como criar buffers de memória e passar argumentos para o kernel.

Referências

Licença

Este repositório de pesquisa está licenciado sob a Licença MIT.

About

Repositório de Pesquisa e Documentação da biblioteca OpenCL (Open Computing Language)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published