ARM NEON com Raspberry Pi 2 e Beaglebone Black

NEON

ARM NEON é uma extensão da arquitetura ARM que conta com um set de instruções exclusivo para realização de rotinas otimizadas com foco principal no processamento de imagens e sinais em geral. Está presente nos cores ARM Cortex-A7 (Raspberry Pi 2), ARM Cortex-A8 (Beaglebone Black) e também em todo o resto da família ARM Cortex-A. Bibliotecas famosas como o OpenCV e a libjpeg-turbo já vêem implementando otimizações com ARM NEON e conseguindo uma aceleração de até 4x em determinados algoritmos.

 

O motor do ARM NEON trabalha com a paralelização na execução de instruções, manipulando dessa forma até 16 dados por registrador de uma só vez. E não é coincidência caso você esteja achando esse recurso parecido com o SIMD descrito no post Explorando algumas instruções SIMD dos microcontroladores ARM Cortex M4 do Felipe Neves, pois a ideia por trás desses recursos é a mesma, e inclusive o nome não "marketizado" do NEON é justamente Advanced SIMD.

 

Os registradores utilizados pelo NEON são de 64 ou 128 bits e podem ser trabalhados da maneira que quisermos utilizando variáveis padrões de 8, 16, 32 ou 64 bits, podendo inclusive até ser com ponto flutuante. É possível por exemplo trabalhar com 4 variáveis de 32 bits por registrador de uma vez só utilizando um registrador de 128, como demonstrado na ilustração abaixo.

 

Exemplo de instrução NEON
Figura 1 - Exemplo de instrução NEON

 

As instruções do NEON se classificam quanto a suas funcionalidades que podem ser de lógica e comparação, processamento geral de dados, aritmética geral, multiplicação, rotacionamento, load/store e as de interação com a unidade de VFP (Vector Floating Point).

 

 

Utilização

 

Para usarmos os recursos oferecidos pelo NEON em linguagem C/C++ temos duas opções. A primeira é utilizarmos vectorizing compiler com as flags "-mfpu=neon" e "-ftree-vectorize" para o GCC, colocando a palavra chave "__restrict" nos ponteiros que queremos que o compilador tente otimizar.

 

A segunda maneira, e mais interessante, é assumirmos as rédeas da situação fazendo uso das funções NEON Intrinsics com o arquivo header arm_neon.h e apenas com a flag "-mfpu=neon". Utilizaremos esse último método para nossos exemplos.

 

 

O primeiro programa com NEON

 

O nosso primeiro exemplo será idêntico tanto para rodar na Raspberry Pi 2 quanto na Beaglebone Black. O objetivo será somar os elementos de mesmo índice entre dois vetores, e armazenar o resultado em um terceiro vetor.

 

O programa se fosse feito de maneira convencional seria assim:

 

 

Agora vamos passo-a-passo adotando a NEON Intrinsics para otimizar o código. Primeiro adicionamos o include para utilizarmos o header do NEON Intrinsics:

 

 

Depois declaramos duas estruturas de 128 bits que serão utilizadas em nossas operações. Cada uma será organizada em 16 lanes de unsigned int de 8 bits.

 

 

Agora a última e principal mudança no nosso loop responsável por executar as somas:

 

 

  • Podemos notar que agora nosso loop incrementa a variável i de 16 em 16 e não mais de 1 em 1. Isso porque em cada iteração, atuamos em 16 posições de cada vetor de uma vez só;
  • A função vldlq_u8 é utilizada para carregar 16 bytes de um determinado vetor para uma estrutura de 128 bits que foi declarada anteriormente;
  • A vaddq_u8 serve para efetuar a soma entre as duas estruturas de 128 bits, tratando individualmente cada byte como uint8_t;
  • E por último vstlq_u8 é utilizado para armazenar a estrutura com a resposta em um vetor convencional.

 

O código final ficará assim:

 

 

Para compilar e rodar o código na sua placa basta executar:

 

 

Lembrando que estamos fazendo uma compilação embarcada no próprio target, no caso em nossas Raspberry Pi 2Beaglebone Black. Também é possível ser feita uma cross-compilação com NEON sem nenhuma dificuldade, bastando que a toolchain também tenha suporte ao NEON.

 

 

Elaborando um pouco mais com NEON

 

Para o segundo exemplo, vamos fazer algo um pouco mais elaborado, um programa para fazer a média dos elementos de mesmo índice entre dois vetores, e armazenar o resultado em um terceiro vetor.

 

E para termos noção do efeito prático dessa otimização vamos rodar o código com vetores gigantes, e também marcar o tempo decorrido na nossa rotina convencional e na otimizada para comparação.

 

Vou deixar o programa em um repositório no GitHub para o post não ficar muito poluído, e vou mostrar aqui apenas o trecho importante, onde faço a média entre cada par de elemento utilizando NEON.

 

 

Para baixar e executar nosso código na sua placa, basta seguir os comandos abaixo:

 

 

Obs: É importante que para execução do teste você tenha o mínimo de processos rodando no sistema, a fim de que eles não interfiram muito no nosso experimento.

 

 

Resultado na Raspberry Pi 2

 

Para a realização desse teste foi utilizada uma Raspberry Pi 2 Model B com Raspbian Wheezey.

 

Para aprender como gravar uma imagem na sua Raspberry Pi 2 vou deixar um link aqui para um outro post do site que trata especificamente disso.

 

 

 

Resultado na Beaglebone Black

 

Para a realização desse teste foi utilizada uma Beaglebone Black Revisão C com Debian 7.5 e rodando a 1GHz.

 

Para aprender como gravar uma imagem na sua Beaglebone Black vou deixar um link aqui para um outro post do site que trata especificamente disso.

 

 

 

Conclusões

 

  • Comprovamos a eficácia da extensão NEON em nosso programa com uma aceleração de 3 a 4 vezes na nossa rotina;
  • Com uma análise simples dos nossos códigos, conseguimos deduzir que quanto maior forem as variáveis trabalhadas menos o NEON será eficiente, visto que menos operações paralelas serão executadas de cada vez;
  • Também é possível observar que quanto mais os dados forem trabalhados após serem carregados nos registradores do NEON mais eficiente o algoritmo ficará, visto que as instruções de load/store ficaram com um tempo cada vez menos considerável dentro da rotina.

 

 

Referências

 

  1. ARM® C Language Extensions Release 1.1
NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Deixe um comentário

avatar
 
  Notificações  
Notificar