desempenho de sistemas de irrigação pivô central - LSCAD
Transcrição
desempenho de sistemas de irrigação pivô central - LSCAD
GERAÇÃO DE BACK-END LLVM E EXPERIMENTOS COM GERAÇÃO DE CÓDIGO PARA O PROCESSADOR ρ-VEX Ricardo Espindola de Aguiar1 Ricardo Ribeiro dos Santos2 1 Aluno do Curso de Engenharia de Computação da UFMS, bolsista de Iniciação Científica CNPq – PIBIC 2012/13 2 Professor da UFMS, Faculdade de Computação; e-mail: [email protected] Resumo: Este trabalho teve por objetivo analisar os impactos do algoritmo de escalonamento de instruções e alocação de registradores baseado no isomorfismo de subgrafos, além de validar e avaliar o back-end LLVM implementado para o processador ρ-vex. Para a análise do algoritmo de isomorfismo de subgrafos, que foi implementado no back-end LLVM para o processador MIPS, foi utilizado o simulador implementado sobre a linguagem de descrição de processadores ArchC. A simulação dos programas gerados utilizando esse algoritmo foi comparada com a dos programas gerados utilizando algoritmos clássicos presentes no LLVM, greedy e linearscan. Para a validação e avaliação do back-end LLVM para ρ-vex foi utilizado o conjunto de ferramentas para arquiteturas VLIW VEX Toolchain. A validação foi feita comparando-se o valor de retorno dos programas gerados com os compiladores LLVM, GCC e VEX C Compiler (presente no VEX Toolchain). A avaliação do back-end foi feita comparando-se os programas gerados com o LLVM e VEX C Compiler. Os resultados dos experimentos de análise do algoritmo de isomorfismo de subgrafos mostram um desempenho similar dos programas gerados pelos três algoritmos, com pequeno ganho que se deve à passagem de parâmetros utilizando registradores e utilização de registradores além da definição da ABI. Os resultados dos experimentos com o back-end LLVM para o ρ-vex mostram que a maioria dos programas tem desempenho inferior em relação ao VEX C Compiler, pois este compilador gera código para arquiteturas VEX em geral, não considerando as limitações que o processador ρ-vex tem em relação à ISA VEX. Já os casos em que o LLVM tem desempenho superior ocorrem devido à taxa de OPI dos programas ser maior que daqueles gerados com o VEX C Compiler. Palavras-chave: isomorfismo de subgrafos, VLIW, experimentos 1. INTRODUÇÃO A geração de código eficiente é um problema bastante conhecido das áreas de compiladores e arquitetura de computadores. Um outro desafio é fornecer ferramentas que possam gerar código para processadores. Assim, se faz necessário otimizar etapas dessas ferramentas na geração de código, que no contexto deste trabalho, são o escalonamento de instruções e alocação de registradores [3]. O escalonamento de instruções tem por objetivo atribuir operações do programa aos recursos físicos do processador, de tal forma que este os execute em uma determinada ordem. A alocação de registradores consiste em mapear os registradores virtuais nos registradores físicos da arquitetura alvo. Quando não há registradores disponíveis, deve-se carregar o conteúdo de um registrador na memória (load), e depois de usá-lo recuperar seu conteúdo (store). Esse processo é chamado de spill e o que se busca com um bom alocador de registradores é diminuir o spill, pois assim diminui o número de instruções e acessos à memória. Uma ferramenta muito útil para geração de código é o LLVM (Low Level Virtual Machine) [1], uma plataforma de compilação de código aberto com o propósito de ser um compilador multiuso, com diversas ferramentas reutilizáveis. Assim, este trabalho teve por objetivo a validação de back-end e avaliação de desempenho de um algoritmo integrado de escalonamento de instruções e alocação de registradores, junto à infraestrutura do back-end da plataforma de compilação LLVM. Especificamente, foi avaliado o algoritmo de escalonamento de instruções e alocação de registradores baseado no isomorfismo de subgrafos [4,5] implementado no back-end LLVM para o processador MIPS, além de ter sido validado e avaliado o back-end LLVM implementado para o processador reconfigurável VLIW (Very Long Instruction Word) ρ-vex [2,7]. 2. MATERIAL E MÉTODOS Este trabalho foi desenvolvido no Laboratório de Sistemas Computacionais de Alto Desempenho (LSCAD) da UFMS em Campo Grande, e foi dividido em duas partes: 1) análise de desempenho do algoritmo de escalonamento de instruções e alocação de registradores baseado no isomorfismo de subgrafos; 2) validação e avaliação do back-end LLVM para o processador ρ-vex. Antes de se adentrar no desenvolvimento do trabalho, será dada uma breve explicação sobre o funcionamento do LLVM. 2.1 Infraestrutura de compilação LLVM No começo dos anos 2000, a maioria dos compiladores era modelada de forma que seu uso era muito específico, ou seja, compilava determinada linguagem para determinada arquitetura. Além disso, o reúso de ferramentas de um compilador estático para um compilador não-estático era difícil. Os poucos compiladores que fugiam à essa regra tinham o código pouco compartilhado. Diante disso nasceu o LLVM, uma coleção modular de componentes reutilizáveis para construção de compiladores e ferramentas para análise e otimização de código executável. O que permite o reúso de código no LLVM é a utilização de uma linguagem intermediária (LLVM IR) na fase do otimizador, entre as fases do front-end e back-end do compilador. Assim, para gerar código a partir de uma linguagem específica é necessário implementar apenas o front-end, e analogamente, para se gerar código para uma arquitetura específica implementa-se apenas o back-end, ao invés do compilador inteiro. A Figura 1 apresenta de maneira simples a organização funcional do compilador LLVM. Figura 1: Organização do Front-end e Back-end da infraestrutura LLVM Um back-end LLVM consiste em um conjunto de componentes reutilizáveis responsáveis por transformar o código na representação intermediária em código assembly ou diretamente em código binário executável. A Figura 2 mostra os estágios do back-end LLVM para geração de código. Figura 2: Estágios do back-end LLVM na geração de código O entendimento detalhado do funcionamento do LLVM foi essencial para o desenvolvimento do trabalho. 2.2 Análise de desempenho do algoritmo de escalonamento de instruções e alocação de registradores baseado no isomorfismo de subgrafos Nesta seção será apresentado o problema do isomorfismo de subgrafos, bem como a metodologia utilizada para se atingir os objetivos referentes ao algoritmo. 2.2.1 Algoritmo de escalonamento de instruções e alocação de registradores baseado no isomorfismo de subgrafos No problema de isomorfismo de grafos, dois grafos G₁= (V₁, E₁) e G₂= (V₂, E₂) são ditos isomorfos, denotado por G₁ G₂, se existe uma bijeção φ: V₁ → V₂ tal que, para todo par de vértices vi, vj ∈ V₁ vale que (vi, vj) ∈ E₁ se e somente se (φ(vi), φ(vj)) ∈ E₂. No problema de isomorfismo de subgrafos, o grafo G₁ = (V₁, E₁) é isomorfo ao grafo G₂ = (V₂, E₂) se existe um subgrafo de G₂, por exemplo, G'₂, tal que G₁ G'₂. No contexto deste relatório, denomina-se G₁ de grafo de entrada e G₂ de grafo base. [4] (a) Grafo G₁ (b) Grafo G₂ (c) Grafo G'₂ Figura 3: Exemplo de isomorfismo de subgrafos [4] O grafo base é um grafo que representa a arquitetura alvo. A Figura 4 mostra o grafo base referente à arquitetura MIPS. Para simplificar, foram representados apenas quatro registradores e as duas unidades funcionais. O grafo base é redimensionado quando um subgrafo isomorfo ao grafo de entrada não é encontrado, de modo a aumentar as chances de se obter o isomorfismo. Figura 4: Grafo base do MIPS representado com quatro registradores [5] A Figura 5 mostra o isomorfismo entre o DAG do trecho de código abaixo e um subgrafo do grafo base. DAGs (Directed Acyclic Graph) são grafos dirigidos que não possuem ciclos. Utilizando a ordenação topológica no DAG, tem-se que suas arestas apontam sempre para frente. Código: (a) addu $2, $3, $4 and $6, $2, $5 Grafo de entrada (b) Grafo base do MIPS (c) Grafo resultante Figura 5: Exemplo de isomorfismo de subgrafos no grafo base do MIPS Os vértices quadrados do DAG de entrada representam as instruções e os vértices circulares representam os registradores que são dados como fonte e destino das instruções. Observe que o grafo base da arquitetura teve de ser redimensionado de forma que pudesse haver um subgrafo isomorfo ao DAG de entrada. [5] 2.2.2 Metodologia para análise de desempenho Os experimentos foram realizados com programas dos Benchmarks SimpleBench e Shootout, e foram compilados utilizando o compilador LLVM build 2.9 e gerados considerando o back-end disponibilizado para o processador MIPS. Os programas gerados utilizando o algoritmo de isomorfismo de subgrafos foram comparados com aqueles gerados utilizando dois algoritmos clássicos presentes no LLVM, greedy e linearscan. A execução do código dos programas foi realizada utilizando-se um simulador MIPS implementado sobre a linguagem de descrição de processadores ArchC [8], onde foi adicionado código para contagem da quantidade de instruções loads e stores executadas. Os passos da simulação são mostrados na Figura 6. O arquivo assembly gerado pelo LLVM é passado como parâmetro para um cross-compiler, que gera o arquivo executável a ser usado na simulação. Figura 6: Fluxo da simulação dos programas 2.3 Validação e avaliação do back-end LLVM para o processador ρ-vex Nesta seção serão mostrados os métodos utilizados para a validação e avaliação do back-end LLVM para o ρ-vex. 2.3.1 O processador ρ-vex O ρ-vex é um processador VLIW (Very Long Instruction Word) de código aberto, que permite a extensibilidade de sua arquitetura junto a operações reconfiguráveis. A arquitetura do processador ρ-vex é baseada na ISA (Instruction Set Architecture) VEX (VLIW Example). O processador ρ-vex implementa uma arquitetura Harvard (memória de dados é separada fisicamente da memória de instruções), não possui unidade de gerenciamento de memória nem unidade para cálculo de números de ponto-flutuante, o endereçamento na memória é realizado com referências absolutas, não há implementação de caches, mecanismos de predição de desvios e não há registradores distribuídos. A microarquitetura do ρ-vex, mostrada na Figura 7, consiste em uma via de dados de quatro estágios, onde operações são buscadas, decodificadas e despachadas para unidades de execução individuais. Após processadas, os resultados das instruções são armazenados na memória de dados ou conjunto de registradores. O ρ-vex dispõe das seguintes unidades funcionais: quatro unidades lógicas e aritméticas (ALUs), duas unidades de multiplicação 16 por 32 bits (MULs), uma unidade de controle (CTRL) e uma unidade de memória (MEM). Figura 7: Organização do processador ρ-vex 2.3.2 Metodologia para validação e avaliação do back-end O processo de validação do back-end consistiu na geração de código para programas do Benchmark Trimaran Compiler Suite (Simple Benchmarks) e implementações de algoritmos clássicos de ordenação e manipulação de estrutura de dados. Os experimentos foram realizados utilizando o VEX Toolchain (disponiblizado pela HP) [6], um conjunto de ferramentas de geração de código e simulação, que contém um compilador C, bibliotecas ANSI C, ferramentas para facilitar o profiling de código, um simulador em software e o montador ρ-ASM. Assim, foram comparados os programas gerados com o back-end LLVM para o processador ρ-vex e com o compilador C presente no VEX Toolchain. A Figura 8 apresenta o fluxo de validação do back-end LLVM. Figura 8: Fluxo de validação do back-end LLVM para o ρ-vex Um programa em C é compilado com o GCC, LLVM e VEX C Compiler. O GCC já emite um binário executável, enquanto que os outros compiladores emitem códigos assembly, que serão as entradas do simulador VEX. A Figura 9 mostra os passos realizados pra se gerar um binário executável para a arquitetura da máquina na qual está instalado o VEX Toolchain. Figura 9: Fluxo da geração de executável no VEX Toolchain O assembly é traduzido em um arquivo cs.c (simulador compilado VEX). Este arquivo é então passado como parâmetro para o GCC, onde é criado um arquivo objeto. Finalmente, o compilador CC recebe o arquivo objeto e gera o binário executável. Depois desse processo, os programas foram depurados com o GDB e tiveram suas saídas comparadas. Para que um programa fosse considerado válido, sua saída deveria ser igual à saída dos programas gerados com o GCC e VEX C Compiler. Os dados para a avaliação de desempenho foram obtidos mediante análise do código assembly e de arquivos gerados pelo próprio simulador após a execução dos programas. 3. RESULTADOS E DISCUSSÃO Esta seção apresenta os resultados obtidos sobre a análise de desempenho do algoritmo de escalonamento de instruções e alocação de registradores baseado no isomofismo de subgrafos e sobre a avaliação do back-end LLVM para o processador ρ-vex. 3.1 Resultados da análise de desempenho do algoritmo de isomorfismo de subgrafos Os resultados são apresentados na Tabela 1 e mostram o número de instruções load e store e a quantidade total de instruções executadas. Assim, é possível analisar o desempenho dos programas em cada um dos algoritmos de escalonamento de instruções e alocação de registradores adotados, bem como validar o código gerado por esses algoritmos. Nesse experimento, todas as métricas tiveram resultado igual em três programas (hello, eight e ifthen). Nos demais há uma pequena diferença entre o algoritmo do isomorfismo e os outros dois algoritmos, que se deve ao fato de algumas chamadas de funções nos programas gerados com o isomorfismo obterem seus parâmetros diretamente dos registradores ao invés da pilha e utilizarem registradores além da definição da ABI (Application Binary Interface). Tabela 1: Resultados da execução dos programas compilados com o LLVM. Programa fib2 hello nestedloop dag eight ifthen local_var_test Algoritmo Instruções Executadas Loads Stores Isomorphism 4113 482 405 Greedy 4115 522 445 Linearscan 4115 522 445 Isomorphism 1184 182 167 Greedy 1184 182 167 Linearscan 1184 182 167 Isomorphism 842161063 219 213 Greedy 842162640 347 326 Linearscan 842162640 347 326 Isomorphism 8204 0 0 Greedy 8458 0 0 Linearscan 8458 0 0 Isomorphism 11095 0 0 Greedy 11095 0 0 Linearscan 11095 0 0 Isomorphism 270028 0 0 Greedy 270028 0 0 Linearscan 270028 0 0 Isomorphism 3473 442 375 Greedy 3675 455 388 Linearscan 3673 455 388 Portanto, os resultados mostram que os programas gerados com o algoritmo de isomorfismo de subgrafos têm desempenho similar àqueles gerados com outros algoritmos clássicos. 3.2 Resultados da avaliação do back-end LLVM para o ρ-vex A Tabela 2 apresenta os resultados de caracterização e desempenho sobre os códigos gerados pelo LLVM e pelo VEX C Compiler. Todos os programas presentes na Tabela 2 foram validados. O motivo dos programas gerados pelo back-end LLVM obterem pior desempenho (counting_sort, dag, fib_mem, floyd_warshall, ifthen, local_var_test, longlong, mm_dyn e struct_test) é que alguns desses programas apresentam operações de divisão e módulo que não são suportadas pelo back-end LLVM por não serem totalmente executadas pelo ρ-vex. Portanto, quando possível, são realizadas algumas manipulações a fim de substituir essas operações por operações mais simples, gerando, assim, mais instruções. Além disso, outro fator que afetou os resultados é em relação aos imediatos maiores que nove bits. Tais imediatos não são suportados pelo ρ-vex, mas são definidos pela ISA VEX. Assim, quando ocorrem, são realizadas manipulações para transferir o valor do imediato para um registrador, o que gera mais instruções. Os casos em que o LLVM apresenta desempenho melhor (binary_tree, double_linked_list, linked_list e parms_test) se devem ao fato do código gerado pelo LLVM obter uma taxa de OPI (operações por instrução) maior que o gerado pelo VEX C Compiler, o que ocasiona menos misses na cache. O número maior de OPI se deve às inserções de instruções NOP pelo compilador do VEX Toolchain, que ocorre quando há conflito de dados nas instruções de memória load (2 NOPs) e de comparação (1 NOP). Tabela 2: Resultados dos programas compilados com o LLVM e o VEX C Compiler. Programas Compilador binary_tree LLVM 1418 609 809 1,4 9,09 2,77 VEX C Compiler 1446 575 871 1,27 5,75 2,17 LLVM 1631 793 838 1,37 3,98 2,69 VEX C Compiler 1431 551 880 1,48 4,61 4,48 LLVM 10370 8849 1521 1,43 50 0,2 VEX C Compiler 7681 6416 1265 2,39 50 0,23 LLVM 14495 12600 1895 1,41 0,17 0,17 VEX C Compiler 16814 14306 2508 1,38 0,22 0,19 counting_sort dag double_linked_list Total de Ciclos de Ciclos Ciclos Execução de Stall OPI Miss Miss Memória Memória de de Instruções Dados (%) (%) fib_mem floyd_warshall ifthen linked_list local_var_test longlong mm_dyn nested parms_test struct_test LLVM 977 383 594 1,44 6,92 2,27 VEX C Compiler 859 267 592 1,43 6,82 2,82 LLVM 4170 3168 1002 1,42 1,95 0,75 VEX C Compiler 2939 1965 974 1,48 2,12 1,02 LLVM 430537 370037 60500 1,5 50 0 VEX C Compiler 220530 200031 20499 3,38 50 0,01 LLVM 51608 46811 4797 1,44 0,05 0,04 VEX C Compiler 58500 52931 5569 1,22 0,07 0,04 LLVM 1447 745 702 1,59 8,33 1,08 VEX C Compiler 1173 473 700 1,69 8,26 1,22 LLVM 440 32 408 2 29,41 26,32 VEX C Compiler 387 24 363 1 50 22,22 LLVM 1944 1053 891 1,76 4,46 1,5 VEX C Compiler 1828 905 923 2,05 4,2 1,84 LLVM 2591 1612 979 1,3 17,02 1,01 VEX C Compiler 1810 865 945 2,13 17,02 1,33 LLVM 445 37 408 1,93 21,74 26,32 VEX C Compiler 447 39 408 1,76 21,74 25 LLVM 508 94 414 1,21 10,2 11,9 VEX C Compiler 412 44 368 1,57 50 12,5 Apesar dos resultados mostrarem um desempenho melhor do compilador VEX C, nota-se um potencial do back-end LLVM para arquiteturas VLIW. 4. CONCLUSÕES Este relatório final apresentou a análise de desempenho do algoritmo de escalonamento de instruções e alocação de registradores baseado no isomorfismo de subgrafos e a validação e avaliação do back-end LLVM para o processador VLIW ρ-vex. Em relação à análise de desempenho do algoritmo de isomorfismo, os resultados mostram que o algoritmo, mesmo em programas menores, pode se beneficiar de algumas situações como a passagem de parâmetros por registradores e não pela pilha. Quanto à validação e avaliação do back-end LLVM, mesmo com desempenho pior na maioria dos programas, é possível se perceber um grande potencial, uma vez que as limitações do ρ-vex afetaram bastante os resultados. Como trabalho futuro a este projeto, pretende-se integrar e avaliar o algoritmo de escalonamento de instruções e alocação de registradores baseado no isomorfismo de subgrafos no back-end LLVM implementado para o processador ρ-vex. 5. REFERÊNCIAS [1] Lattner C. LLVM. In: A. Brown, G. Wilson. The Architecture of Open Source Applications: Elegance, Evolution, and a Few Fearless Hacks. Creative Commons, 2011. [2] T. van As, “ρ-VEX: A Reconfigurable and Extensible VLIW Processor”, Master's thesis, Delft University of Technology, 2008. Faculty of Electrical Engineering, Mathematics and Computer Science. [3] L. Santos Silva. Algoritmos para escalonamento de instruções e alocação de registradores na infraestrutura LLVM. Qualificação de Mestrado, Faculdade de Computação – Universidade Federal de Mato Grosso do Sul. [4] R. Santos, R. Azevedo, e G. Araujo. Instruction Scheduling Based on Subgraph Isomorphism for a High Performance Computer Processor. Journal of Universal Computer Science, 14(21):3465-3480, 2008. [5] Silva, Lucas; Silva, Richard; Santos, Ricardo. An Integrated Technique for Instruction Scheduling and Register Allocation Based on Subgraph Isomorphism. In: Proc. of the 16th Brazilian Symposium on Programming Languages, 2012, Natal, Brazil. [6] Hewlett-Packard Laboratories, “VEX Toolchain”. Disponível em: hhtp://www.hpl.hp.com/downloads/vex/, Março 2011. [7] J. A. Fisher, P. Faraboschi, and C. Young, Embedded Computing: A VLIW Approach to Architecture, Compilers and Tools. Elsevier, 2005. [8] T. A. Team, “The ArchC Architecture Description Language”. Disponível em: http://www.archc.sourceforge.net/index.html, Fevereito 2013. 6. PRODUTOS ALCANÇADOS Resumo aceito para apresentação na modalidade pôster na IV Escola Regional de Informática (ERI), Três Lagoas/MS, agosto/2013.