Criando um bot para participar do mini cup da AI. Experiência GPU


Continuação do artigo 1 e do artigo 2 .


Abaixo, abaixo, mostrarei a experiência do autor no uso da GPU para cálculos, inclusive como parte da criação de um bot para participar do mini cup da AI. Mas, em vez disso, este é um ensaio sobre o assunto da GPU.


- Seu nome é mágico ...
-Sabe Joel? A mágica está saindo ...


Na infância, falamos sobre a idade em que a química ainda não está em andamento na escola ou está apenas começando a ocorrer, o autor ficou fascinado com a reação ardente; aconteceu que seus pais não interferiram nele e o terreno baldio de Moscou perto da casa foi ocasionalmente iluminado por flashes de várias atividades infantis, um foguete caseiro em preto pólvora, caramelo de nitrato de açúcar, etc. Duas circunstâncias limitaram as fantasias das crianças: a decomposição da nitroglicerina em um laboratório doméstico com um teto sibilante de ácidos e o trajeto até uma sala da polícia na tentativa de obter produtos químicos em uma das empresas de defesa espalhadas pela área metropolitana de Aviamotornaya.


E então apareceu uma escola de física com computadores yamaha msx, uma calculadora MK programável em casa e não havia tempo para química. Os interesses da criança mudaram para computadores. E o que o autor faltou no primeiro contato com o computador foi a reação ardente, seus programas estavam em chamas, não havia essa sensação de poder natural. Você podia ver o processo de otimização de cálculos em jogos, mas naquela época o autor não sabia como substituir o cálculo de sin () pela tabela de valores dessa função, não havia Internet ...


Assim, o autor foi capaz de obter uma sensação de alegria pelo poder da computação, queima limpa, uso a GPU nos cálculos.


Em um habr, existem alguns bons artigos sobre cálculos na GPU. Também existem muitos exemplos na Internet, por isso foi decidido escrever no sábado de manhã sobre sentimentos pessoais e é possível empurrar outras pessoas para o paralelismo de massas.


Vamos começar com formulários simples. A computação de GPU suporta várias estruturas, mas as mais famosas são NVIDIA CUDA e OpenCL. Tomaremos o CUDA e imediatamente teremos que restringir nosso conjunto de linguagens de programação para C ++. Existem bibliotecas para conectar-se ao CUDA em outras linguagens de programação, por exemplo, ALEA GPU em C #, mas esse é o tópico de um artigo de revisão separado.


Assim como eles não podiam fabricar um carro de massa com um motor a jato ao mesmo tempo, embora alguns de seus indicadores sejam mais altos que os de um motor de combustão interna, nem sempre é possível usar cálculos paralelos em problemas reais. O principal aplicativo para computação paralela: você precisa de uma tarefa que contenha algum elemento de caractere de massa, multiplicidade. No nosso caso de criação de um bot, uma rede neural (muitos neurônios, conexões neurais) cai sob a massa e uma população de bots (calculando a dinâmica do movimento, colisões para cada bot levam algum tempo, se os bots forem de 300 a 1000, o processador central se renderá e você observará apenas lentamente latente do seu programa, como longas pausas entre os quadros de visualização).


A melhor opção de massa é quando cada elemento dos cálculos não depende do resultado dos cálculos em outro elemento da lista, por exemplo, a tarefa simples de classificar uma matriz já está coberta de todos os tipos de truques, pois a posição do número na matriz depende de outros números e não pode ser levada à testa em um ciclo paralelo . Para simplificar a redação: o primeiro sinal de caractere de massa bem-sucedido é que, se você não precisar alterar a posição de um elemento na matriz, poderá realizar cálculos livremente, pegar os valores de outros elementos para isso, mas não movê-lo de seu lugar. Algo como um conto de fadas: não mude a ordem dos elementos, caso contrário, a GPU se transformará em uma abóbora.


Nas linguagens de programação modernas, existem construções que podem ser executadas em paralelo em vários núcleos de um processador central ou threads lógicos e são amplamente usadas, mas o autor concentra o leitor no paralelismo em massa, quando o número de módulos em execução excede centenas ou milhares de unidades.


Os primeiros elementos das estruturas paralelas apareceram: um ciclo paralelo . Para a maioria das tarefas, será suficiente. Em um sentido amplo, esta é a quintessência
computação paralela.


Um exemplo de gravação do loop principal no CUDA (kernel):


int tid = blockIdx.x * blockDim.x + threadIdx.x; int threadN = gridDim.x * blockDim.x; for (int pos = tid; pos < numElements; pos += threadN) { //    pos,     ,       thread     pos.  :    thread    ,  thread   pos=1146     thread c  pos=956.        .           . } 

Muito foi escrito na documentação e nas revisões do CUDA , sobre blocos de GPU, sobre Threads que são produzidos nesses blocos, como paralelizar a tarefa neles. Mas se você tiver uma matriz de dados e ela consistir claramente em elementos de massa, use o formulário de loop acima, pois é visualmente semelhante a um loop regular no formulário, o que é agradável, mas infelizmente não no conteúdo.


Eu acho que o leitor já entende que a classe de tarefas está se estreitando rapidamente em relação à programação paralela em massa. Se estamos falando sobre a criação de jogos, mecanismos de renderização em 3D, redes neurais, edição de vídeo e outras tarefas semelhantes, então a limpeza de ações de leitores independentes está muito desgastada, existem programas grandes, pequenos programas, estruturas, bibliotecas conhecidas e desconhecidas para essas tarefas. Ou seja, a área permanece apenas a partir do tópico, para criar seu próprio pequeno foguete de computação, não o SpaceX e o Roscosmos, mas algo caseiro, mas completamente ruim para os cálculos.



Aqui está uma foto de um foguete completamente flamejante retratado.


Falando de tarefas que um ciclo paralelo em suas mãos não será capaz de resolver. E os criadores da CUDA na pessoa dos desenvolvedores da NVIDIA já pensaram nisso.


Em alguns lugares, existe uma biblioteca Thrust útil até que "nenhuma opção" seja feita de maneira diferente. A propósito, não encontrou sua opinião completa no Habré.


Para entender como funciona, primeiro você precisa dizer três frases sobre os princípios da CUDA. Se precisar de mais palavras, você pode ler o link.


Os princípios da CUDA:


As computações ocorrem na GPU, cujo programa é o kernel, e você deve escrevê-lo em C. O kernel, por sua vez, se comunica apenas com a memória da GPU e é necessário carregar os dados na memória do processador de vídeo do programa principal e enviá-los de volta ao programa. Algoritmos sofisticados no CUDA exigem flexibilidade da mente.


Portanto, a biblioteca Thrust remove a rotina e assume algumas das tarefas "complexas" do CUDA, como somar matrizes ou classificá-las. Você não precisa mais escrever um kernel separado, carregar ponteiros na memória e copiar dados desses ponteiros para a memória da GPU. Todo o mistério acontecerá diante de seus olhos no programa principal e com uma velocidade ligeiramente inferior à CUDA. A biblioteca Thrust é escrita em CUDA, portanto, este é um campo único em termos de desempenho.


O que você precisa fazer no Thrust é criar uma matriz (thrust :: vector) dentro de sua biblioteca, compatível com matrizes regulares (std :: vector). Naturalmente, isso não é tudo tão simples, mas o significado do que o autor disse é semelhante à verdade. Na verdade, existem duas matrizes, uma na GPU (dispositivo) e a outra no programa principal (host).


Um exemplo mostrará a simplicidade da sintaxe (matrizes X, Y, Z):


 // initialize X to 0,1,2,3, .... thrust::sequence(X.begin(), X.end()); // compute Y = -X thrust::transform(X.begin(), X.end(), Y.begin(), thrust::negate<int>()); // fill Z with twos thrust::fill(Z.begin(), Z.end(), 2); // compute Y = X mod 2 thrust::transform(X.begin(), X.end(), Z.begin(), Y.begin(), thrust::modulus<int>()); // replace all the ones in Y with tens thrust::replace(Y.begin(), Y.end(), 1, 10); 

Você pode ver como é inofensivo no contexto da criação do kernel CUDA, e o conjunto de funções no Thrust é grande . Começando do trabalho com variáveis ​​aleatórias, que no CUDA é feito por uma biblioteca cuRAND separada (de preferência executada por um kernel separado), para classificar, somar e escrever suas funções de acordo com a funcionalidade próxima às funções do kernel.


O autor tem pouca experiência usando CUDA e C ++, dois meses. Sobre este ano, sobre C #. Isso, é claro, contradiz levemente o início do artigo sobre seu conhecimento precoce de computadores, física escolar e matemática aplicada como educação. Eu vou dizer. Mas por que estou escrevendo este artigo, não é que eu tenha dominado tudo isso, mas que o C ++ acabou sendo uma linguagem confortável (eu costumava ter um pouco de medo disso, no contexto de artigos no tipo Haber "funções lambda → sobrecarga de operadores internos, como redefinir tudo "), é claro que os anos de seu desenvolvimento levaram a ambientes de desenvolvimento bastante amigáveis ​​(IDEs). A própria linguagem em sua versão mais recente, parece que coleta lixo da memória, não sei como era antes. Pelo menos, os programas escritos pelo autor nas construções algorítmicas mais simples geraram algoritmos computacionais para bots por dias e não houve vazamentos de memória e outras falhas em alta carga. Isso também se aplica à CUDA, a princípio parece complicado, mas é baseado em princípios simples e, é claro, é difícil inicializar locais na GPU em locais, se houver muitos deles, mas você terá seu próprio pequeno foguete, com fumaça da placa de vídeo.


Das classes de objetos para treinamento com a GPU, o autor recomenda autômatos celulares . Houve um aumento de popularidade e moda para eles, mas as redes neurais conquistaram a mente dos desenvolvedores.
Até:


"toda quantidade na física, incluindo tempo e espaço, é finita e discreta".
do que não um autômato celular.


Mas é lindo quando três fórmulas simples podem criar isso:



Se for interessante ler sobre autômatos celulares na CUDA, escreva nos comentários, haverá material digitado para um pequeno artigo.
E esta é a fonte de autômatos celulares (no vídeo, há links para as fontes):



A ideia de escrever um artigo depois do café da manhã, de uma só vez, parece-me dar certo. Segunda hora do café. Tenha um bom leitor de final de semana.

Source: https://habr.com/ru/post/pt417757/


All Articles