
1. Introdução
O UEM é uma linguagem de programação de placa gráfica de alto nível que pode ser incorporada ao código regular na linguagem de programação do sistema Rust .
Este artigo abordará a sintaxe do Emu, seus recursos e também mostrará alguns exemplos ilustrativos de seu uso em código real.
Instalação
- A biblioteca revisada precisa de uma dependência externa do OpenCL. Você precisa instalar o driver apropriado para o seu hardware.
- Cargo.tomltexto abaixo. Isso fará o download das versões disponíveis mais recentes (se você precisar de um assembly específico, em vez de- *coloque a versão necessária):
 
 -  - [dependencies] em = "*" //   Emu ocl = "*" //   OpenCL
 
Sintaxe
A sintaxe da Emu é bastante simples, porque esse idioma é destinado apenas para escrever funções do kernel que são transmitidas no OpenCL durante a compilação.
Tipos de dados
A linguagem Emu possui nove tipos de dados semelhantes aos do Rust. A seguir, é apresentada uma tabela desses tipos de dados:
Variáveis
As variáveis são declaradas usando a palavra-chave let , localizada atrás do identificador, dois pontos, tipo de dados, sinal de igual, valor atribuído e ponto e vírgula.
 let age: i32 = 54; let growth: f32 = 179.432; let married: bool = true; 
Conversões
A conversão de tipos de dados primitivos é realizada usando o operador binário as , seguindo o tipo de destino. Observo que o tipo de destino também pode ser uma unidade de medida (consulte a próxima seção):
 let width: i16 = 324; let converted_width: i64 = width as i64; 
Unidades
A linguagem Emu permite tratar números como unidades de medida, projetadas para simplificar cálculos científicos. Neste exemplo, o length variável length definido inicialmente em metros, mas outras unidades de medida são adicionadas a ele:
 let length: f32 = 3455.345;  
Constantes predefinidas
A UEM possui um conjunto de constantes predefinidas que são convenientes para uso na prática. Abaixo está a tabela correspondente.
Também são definidas constantes correspondentes a dados científicos. Você pode encontrar a tabela que consiste nessas constantes aqui .
Instruções condicionais
As declarações condicionais da UEM são semelhantes às declarações correspondentes no Rust. O código a seguir usa construções condicionais:
 let number: i32 = 2634; let satisfied: bool = false; if (number > 0) && (number % 2 == 0) { satisfied = true; } 
Para loops
O cabeçalho do loop For é definido como for NUM in START..END , onde NUM é uma variável que recebe valores do intervalo [START; END) [START; END) através da unidade.
 let sum: u64 = 0; for i in 0..215 { sum += i; } 
Enquanto loops
O título do loop While é definido como while (CONDITION) , em que CONDITION é a condição para o loop prosseguir para a próxima iteração. Este código é semelhante ao exemplo anterior:
 let sum: u64 = 0; let idx: i32 = 0; while (idx < 215) { sum += idx; idx += 1; } 
Loops sem fim
Loops infinitos não têm uma condição de saída explícita e são definidos pela palavra-chave loop . No entanto, eles podem ser continuados ou interrompidos pelas instruções break e continue (como os outros dois tipos de loops).
 let collapsed: u64 = 1; let idx: i32 = 0; loop { if idx % 2 == 0 { continue; } sum *= idx; if idx == 12 { break; } } 
Retorno da função
Como em todas as outras linguagens de programação, a return é a saída da função atual. Também pode retornar um determinado valor se a assinatura da função (consulte as seções a seguir) permitir isso.
 let result: i32 = 23446; return result; 
Outros operadores
- Operadores de atribuição disponíveis: =,+=,-=,*=,/=,%=,&=,^=,<<=,>>=;
- O operador de índice é [IDX];
- Operador de chamada - (ARGS);
- Operadores unários: *para cancelamento de referência ,! inverter dados booleanos,-negar números;
- Operadores binários: +,-,*,/,%,&&,||,&,|,^,>>,<<,>,<,>=,<=,==,==!=.
Funções
Existem três partes de funções no Emu: o identificador, parâmetros e o corpo da função, consistindo em uma sequência de instruções executáveis. Considere a função de adicionar dois números:
 add(left f32, right f32) f32 { return left + right; } 
Como você deve ter notado, essa função retorna a soma dos dois argumentos passados para ela usando o tipo de dados f32 .
Espaços de endereço
Cada parâmetro da função corresponde a um espaço de endereço específico . Por padrão, todos os parâmetros correspondem ao espaço __private__ .
Adicionar os prefixos global_ e local_ ao identificador de parâmetro indica explicitamente seu espaço de endereço.
A documentação aconselha o uso do prefixo global_ para todos os vetores e não o prefixo de mais nada.
Funções incorporadas
A UEM fornece um pequeno conjunto de funções internas (extraídas do OpenCL) que permitem gerenciar dados da GPU:
- get_work_dim()- Retorna o número de dimensões;
- get_global_size()- Retorna o número de elementos globais para uma determinada dimensão;
- get_global_id()- Retorna o identificador exclusivo do elemento para a dimensão especificada;
- get_global_size()- Retorna o número de elementos globais para uma determinada dimensão;
- get_local_id()- Retorna um identificador exclusivo para um elemento local dentro de um grupo de trabalho específico para uma determinada dimensão;
- get_num_groups()- Retorna o número de grupos de trabalho para uma determinada dimensão;
- get_group_id()- Retorna um identificador exclusivo para o grupo de trabalho.
No código do aplicativo, na maioria das vezes você encontrará a expressão get_global_id(0) , que retorna o índice atual do elemento vetorial associado à chamada para sua função do kernel.
Execução de código
Considere a sintaxe para chamar funções de Emu a partir do código Rust regular. Como exemplo, usaremos uma função que multiplica todos os elementos de um vetor por um determinado número:
 use em::emu; emu! { multiply(global_vector [f32], scalar f32) { global_vector[get_global_id(0)] *= scalar; } } 
Para converter essa função em código OpenCL, você precisa colocar sua assinatura na macro de build! da seguinte maneira:
 use em::build;  
Outras ações se resumem a chamar funções de Emu que você escreveu a partir do código Rust. Não poderia ser mais fácil:
 fn main() { let vector = vec![0.4445, 433.245, 87.539503, 2.0]; let result = multiply(vector, 2.0).unwrap(); dbg!(result); } 
Exemplo de aplicação
Este programa assume um escalar como o primeiro argumento, pelo qual é necessário multiplicar os seguintes argumentos. O vetor resultante será impresso no console:
 use em::{build, emu};  
Você pode executar este código com o comando cargo run -- 3 2.1 3.6 6.2 . A conclusão resultante atende às expectativas:
 [src/main.rs:33] result = [ 6.2999997, 10.799999, 18.599998, ] 
Link para OpenCL
Como mencionado anteriormente, o Emu é apenas uma abstração do OpenCL e, portanto, tem a capacidade de interagir com o oclate. O código abaixo é retirado de um exemplo no repositório oficial :
 use em::emu;  
Conclusão
Espero que tenham gostado do artigo. Você pode obter uma resposta rápida para suas perguntas no bate-papo em russo no Rust ( versão para iniciantes ).