Avaliar o desempenho para Linux on POWER

Analisar o desempenho utilizando ferramentas Linux

Aprenda a avaliar os problemas de desempenho do Linux on POWER® que têm como foco ambientes de linguagem compilada (como C ou C++). Este artigo explica o modelo POWER7® CPI e demonstra o uso de ferramentas Linux geralmente disponíveis® a fim de mostrar possíveis paralisações de CPU, riscos de pipeline e problemas de desempenho. Analisar e otimizar um algoritmo para o POWER7 é a seção final.

Adhemerval Zanella Netto, Software Engineer, IBM

Photo of Adhemerval Zanella photoAdhemerval Netto é um Software Engineer experiente no IBM Linux Technology Center e trabalha com análise de desempenho e desenvolvimento de cadeia de ferramentas na arquitetura do Power. É responsável por diversos relatórios de desempenho do Power com o Linux no spec.org e contribuiu para o software livre (glibc).



Ryan S. Arnold, Advisory Software Engineer, IBM

Photo of Ryan S. ArnoldRyan Arnold é IBM Advisory Software Engineer do IBM Linux Technology Center. Tem experiência em integração de cadeia de ferramentas GNU e no desenvolvimento de bibliotecas do sistema Linux. Ryan é Power Architecture Maintainer de GLIBC e Primary Library Maintainer de Libdfp. Também é coautor do "Power Architecture 32-bit Application Binary Interface Supplement 1.0".



16/Mai/2014

Introdução

A avaliação do desempenho do aplicativo pode ser uma tarefa complexa em máquinas modernas. Ferramentas geralmente disponíveis dificilmente manipulam todas as variáveis de desempenho. Cada carga de trabalho sobrecarrega um subsistema diferente do computador. A medida e o ajuste de um programa limitado por CPU são relativamente diferentes do ajuste de um programa limitado por ES ou por memória. Neste artigo, damos ênfase aos programas limitados por CPU e memória em ambientes de linguagem compilada (C, C++ e outros). Demonstramos como:

  • Encontrar pontos de acesso do programa (uma região, função, método em um programa no qual uma alta proporção das instruções executadas ocorre)
  • Medir como o programa se comporta no POWER7 utilizando o contador de desempenho do hardware disponível no processador
  • Identificar as ferramentas utilizadas para a avaliação de desempenho no Linux.

Modelo de CPI para o POWER7

O entendimento de uma análise de desempenho do aplicativo começa com uma discussão da Métrica do CPI. A métrica Ciclos por Instrução (CPI) é o número de ciclos do processador necessário para concluir uma instrução. Cada instrução é decomposta em diversos estágios: um pipeline RISC clássico terá um estágio de busca de instrução seguido pela busca de decodificação/registro da instrução, um acesso ideal à memória e, finalmente, o writeback. Uma CPU pode melhorar sua métrica de CPI (medida por um valor de CPI mais baixo) explorando o paralelismo do nível de instrução: Cada estágio irá manipular diferentes instruções em estágios diferentes. Ao otimizar, tente minimizar o valor de CPI para maximizar a utilização do sistema. Figura 1 mostra um fluxo de instrução ideal em um processador de pipeline.

Figura 1. Fluxo de instrução ideal de um processador de pipeline

Às vezes, um estágio não é totalmente independente de outros estágios ou ele emite uma instrução com dependências que obrigam o processador a atender a essa demanda antes de continuar sua execução. Por exemplo, uma carga de memória seguida por uma instrução aritmética faz com que o processador primeiro busque os dados no cache ou na memória e somente depois disso emita a instrução aritmética. Quando isso acontece, diz-se que o pipeline do processador encontra uma paralisação, o que paralisa o pipeline. Figura 2 mostra com o que se parece um pipeline paralisado.

Figura 2. Um processador de pipeline com paralisações

Nos exemplos na Figura 1 e Figura 2, considere que durante 11 ciclos de operação em um pipeline totalmente preenchido (no qual uma instrução é concluída por ciclo) o processador possa executar oito instruções. No entanto, quando ocorre uma paralisação de três ciclos, somente cinco instruções foram executadas no mesmo número de ciclos. A perda de desempenho é de cerca de 40%. Dependendo do algoritmo, algumas paralisações são inevitáveis; no entanto, uma análise cuidadosa pode fornecer sugestões e conselhos sobre como reescrever ou ajustar algumas partes do código a fim de evitar essas paralisações. Encontre uma explicação mais completa e didática sobre o enfileiramento moderno da CPU e o paralelismo no nível de instrução no artigo "Modern Microprocessors - a 90 minute guide" (consulte Recursos).

O CPI Breakdown Model (CBM) relaciona os estágios do processador funcional aos contadores de desempenho a fim de mostrar qual unidade funcional da CPU está gerando paralisações. O CBM depende da arquitetura da CPU e do modelo do processador; as Arquiteturas Power e Intel apresentam CBMs completamente diferentes. O POWER5 CBM, embora semelhante, é diferente do POWER7 CBM. Figura 3 mostra uma parte do POWER7 CBM. (Consulte uma versão de texto dessas informações.)

Figura 3. POWER 7 CBM parcial

Na Arquitetura Power, os contadores de desempenho do hardware são um conjunto de registros para fins especiais cujo conteúdo é atualizado quando ocorre um determinado evento no processador. O processador POWER7 possui uma Unidade de Monitoramento de Desempenho (PMU) integrada com seis Monitores de Contador de Desempenho (PCMs) de nível de encadeamento por PMU. Quatro deles são programáveis, isto é, é possível monitorar quatro eventos ao mesmo tempo e há mais de 500 eventos de desempenho possíveis. Os contadores de desempenho do POWER7 são definidos por grupos e o PMU pode observar somente eventos do mesmo grupo por vez. Figura 3 mostra um subconjunto dos contadores de desempenho utilizados para definir o POWER7 CBM. Os contadores da Figura 3 que aparecem depois de um perfil são utilizados para indicar qual unidade funcional da CPU está causando a paralisação do processador e fornecem sugestões de como sintonizar o algoritmo para eliminá-los.

Na Figura 3, as caixas brancas são POWER7 PCMs específicos assistidos em um perfil. Com base em seus valores, as caixas cinzas [cada uma marcada com um asterisco (*)] são calculadas (essas métricas não têm nenhum contador de hardware específico).

Observação: Encontre uma referência abrangente da PMU para o POWER7 no artigo "Comprehensive PMU Event Reference POWER7" (consulte Recursos).


Ferramentas no Linux

Como o PCM encontrado em processadores POWER7 pode ser utilizado? Embora seja possível utilizar vários métodos de criação de perfil no POWER, como interrupções de hardware, instrumentação de códigos (como gprof), ganchos do sistema operacional (systemtap); o PCM fornece um conjunto extenso de contadores que trabalham diretamente com a funcionalidade do processador. O gerenciador de perfis do PCM apresenta amostras dos valores de registro do processador de forma constante, em intervalos regulares, utilizando as interrupções do sistema operacional. Embora a criação de perfis de amostra possa produzir resultados precisos de forma menos numérica do que os resultados de rastreio da instrução, ela tem menos impacto no desempenho geral do sistema e permite que a referência de destino seja executada em velocidade próxima à máxima. Os dados resultantes não são exatos; são uma aproximação com uma margem de erro.

As duas ferramentas mais utilizadas para a criação de perfil do PCM no Linux são OProfile e perf (consulte Recursos). Embora as duas utilizem o mesmo princípio, constantemente produzindo amostras do registro especial de hardware (por meio de um syscall) juntamente com um registro de chamadas da carga de trabalho, cada uma é configurada e utilizada de forma diferente.

A ferramenta OProfile é um gerenciador de perfis de todo o sistema para sistemas Linux, capaz de criar perfis de todo o código em execução com baixo gasto adicional. Ela consiste em um motivador de kernel e um daemon para a coleta de dados de amostra e diversas ferramentas pós-criação de perfis para transformar dados em informações. Símbolos de depuração (opção -g para gcc) não são necessários, a menos que queira a origem anotada. Com um kernel Linux 2.6 recente, OProfile pode fornecer informações de criação de perfil de árvore de chamadas estilo gprof. OProfile apresenta um gasto adicional típico de 1-8%, dependendo da frequência de amostragem e da carga de trabalho.

No POWER, OProfile funciona assistindo grupos de contadores de hardware de desempenho e contadores de desempenho, embora diferentes grupos possam ser utilizados juntos. Isso significa que a obtenção de diferentes contadores de desempenho a partir da mesma carga de trabalho requer sua execução diversas vezes com diferentes configurações de evento OProfile . Isso também significa que não é possível assistir todo o POWER7 CBM ao mesmo tempo. Os grupos disponíveis são definidos no documento “POWER7 PMY Detailed Event Description”, mencionado acima, ou por meio da execução do comando na Lista 1:

Lista 1. Listagem de grupos do OProfile
# opcontrol -l

Os comandos na Lista 2 demonstram uma simples configuração e chamada OProfile :

Lista 2. Configuração de ciclos do OProfile POWER7 CPU
# opcontrol -l
# opcontrol -–no-vmlinux
# opcontrol -e PM_CYC_GRP1:500000 -e PM_INST_CMPL_GRP1:500000 -e PM_RUN_CYC_GRP1:500000 
-e PM_RUN_INST_CMPL_GRP1:500000
# opcontrol --start

Execute a carga de trabalho na Lista 3.

Lista 3. Sequência de comandos de execução do OProfile
# opcontrol --dump 
# opcontrol –-stop
# opcontrol --shutdown

A fim de obter o relatório do contador de desempenho, emita o comando na Lista 4:

Lista 4. Geração de relatórios do OProfile
# opreport -l > workload_report

Observação: Encontre um guia abrangente do OProfile (embora não atualizado para o POWER7) no artigo do developerWorks "Identify performance bottlenecks with OProfile for Linux on POWER" (consulte Recursos).

A ferramenta perf , apresentada no kernel do Linux 2.6.29, analisa os eventos de desempenho nos níveis do hardware e do software. A ferramenta perf possui a vantagem de ser orientada ao programa, em vez de orientada ao sistema, como o OProfile. Ela tem algumas listas de contadores de desempenho pré-configuradas, como 'cpu-cycles OU cycles', 'branch-misses' ou 'L1-icache-prefetch-misses' e tem a capacidade de multiplexar os grupos de PMU a fim de permitir a reunião de diversos contadores de desempenho a partir de diferentes grupos ao mesmo tempo à custa da precisão da amostra.

Uma desvantagem é que, embora ele permita a reunião de contadores de desempenho de hardware de forma direta, perf não reconhece o nome do contador denotado pelo POWER7 CBM; em vez disso, precisa utilizar números hexadecimais brutos. Tabela 1 é um meapeamento dos eventos do OProfile para números hexadecimais que podem ser utilizados com o perf (utilizando as opções de eventos brutos de registro) para utilizar o CBM para POWER7.

Tabela 1. Códigos brutos de eventos perft do POWER7

ContadorCódigo bruto
PM_RUN_CYC200f4
PM_CMPLU_STALL 4000a
PM_CMPLU_STALL_FXU20014
PM_CMPLU_STALL_DIV40014
PM_CMPLU_STALL_SCALAR40012
PM_CMPLU_STALL_SCALAR_LONG20018
PM_CMPLU_STALL_VECTOR2001c
PM_CMPLU_STALL_VECTOR_LONG4004a
PM_CMPLU_STALL_LSU20012
PM_CMPLU_STALL_REJECT40016
PM_CMPLU_STALL_ERAT_MISS40018
PM_CMPLU_STALL_DCACHE_MISS20016
PM_CMPLU_STALL_STORE2004a
PM_CMPLU_STALL_THRD1001c
PM_CMPLU_STALL_IFU4004c
PM_CMPLU_STALL_BRU4004e
PM_GCT_NOSLOT_CYC100f8
PM_GCT_NOSLOT_IC_MISS2001a
PM_GCT_NOSLOT_BR_MPRED4001a
PM_GCT_NOSLOT_BR_MPRED_IC_MISS4001c
PM_GRP_CMPL30004
PM_1PLUS_PPC_CMPL100f2

Observação: Encontre um guia abrangente do perf (embora não atualizado para o POWER7) no Wiki da IBM "Using perf on POWER7 systems" (consulte Recursos).

É possível obter os códigos brutos utilizados com o perf que corresponde aos eventos do POWER7 definidos no OProfile a partir do libpfm4 (consulte Recursos): Eles são definidos no cabeçalho específico do POWER7 (lib/events/power7_events.h). O programa de exemplo examples/showevtinfo também mostra os nomes do evento e os códigos hexadecimais brutos correspondentes.

A fim de obter informações do contador, a criação de perfil é uma abordagem comum. A criação de perfil permite que um desenvolvedor identifique pontos de acesso na execução do código e no acesso aos dados, encontre áreas sensíveis de desempenho, entenda os padrões de acesso da memória, entre outros. Antes de começar a traçar o perfil, é necessário elaborar uma estratégia de avaliação de desempenho. O programa pode ser composto de vários módulos e/ou objetos compartilhados dinâmicos (DSO), pode utilizar o kernel de forma intensiva, pode depender mais do acesso ao padrão de dados (alta pressão no acesso ao cache L2 ou L3) ou pode dar ênfase às unidades de operação do vetor. A próxima seção dará ênfase às possíveis estratégias de avaliação de desempenho.

Estratégias para a avaliação de desempenho

Uma avaliação inicial de desempenho é encontrar os pontos de acesso do programa inspecionando o contador de utilização do ciclo da CPU. Para fazer isso no POWER7, assista aos eventos listados na Tabela 2:

Tabela 2. Contadores de utilização do ciclo do POWER7 CPU

ContadorDescrição
PM_CYCCiclos do Processador
PM_INST_CMPLNúmero de Instruções PowerPC concluídas
PM_RUN_CYCCiclos do Processador bloqueados pela trava de execução. Os sistemas operacionais utilizam a trava de execução para indicar quando eles estão fazendo um trabalho útil. A trava de execução é normalmente limpa no loop inativo do S.O. O bloqueio pela trava de execução filtra o loop inativo.
PM_RUN_INST_CMPLNúmero de instruções de execução concluídas

A execução de OProfile com esses eventos mostrará o tempo geral que o processador levou em um símbolo. Segue uma saída de perfil de exemplo para o componente 403.gcc a partir do conjunto de referência SPECcpu2006 compilado com o IBM Advance Toolchain 5.0 para POWER (consulte Recursos). Segue a saída a partir do comando opreport -l.

Lista 5. Saída a partir do 'opreport -' para a referência 403.gcc (contador PM_CYC_GRP1 e PM_INST_CMPL_GRP1)
CPU: ppc64 POWER7, speed 3550 MHz (estimated) 
Counted PM_CYC_GRP1 events ((Group 1 pm_utilization) Processor Cycles) with a unit 
mask of 0x00 (No unit mask) count 500000 
Counted PM_INST_CMPL_GRP1 events ((Group 1 pm_utilization) Number of PowerPC 
Instructions that completed.) with a unit mask of 0x00 (No unit mask) count 500000 

samples  %        samples  %        image name      app name        symbol name 
204528    7.9112  32132     1.3848  gcc_base.none   gcc_base.none   reg_is_remote_cons\
tant_p.isra.3.part.4 
125218    4.8434  246710   10.6324  gcc_base.none   gcc_base.none   bitmap_operation 
113190    4.3782  50950     2.1958  libc-2.13.so    libc-2.13.so    memset 
90316     3.4934  22193     0.9564  gcc_base.none   gcc_base.none   compute_transp 
89978     3.4804  11753     0.5065  vmlinux         vmlinux         .pseries_dedicated_\
idle_sleep 
88429     3.4204  130166    5.6097  gcc_base.none   gcc_base.none   bitmap_element_\
allocate 
67720     2.6194  41479     1.7876  gcc_base.none   gcc_base.none   ggc_set_mark 
56613     2.1898  89418     3.8536  gcc_base.none   gcc_base.none   canon_rtx 
53949     2.0868  6985      0.3010  gcc_base.none   gcc_base.none   delete_null_\
pointer_checks 
51587     1.9954  26000     1.1205  gcc_base.none   gcc_base.none   ggc_mark_rtx_\
children_1 
48050     1.8586  16086     0.6933  gcc_base.none   gcc_base.none   single_set_2 
47115     1.8224  33772     1.4555  gcc_base.none   gcc_base.none   note_stores
Lista 6. Saída a partir do 'opreport -' para a referência 403.gcc (contador PM_RUN_CYC_GRP1 e PM_RUN_INST_CMPL_GRP1)
Counted PM_RUN_CYC_GRP1 events ((Group 1 pm_utilization) Processor Cycles gated by the 
run latch.  Operating systems use the run latch to indicate when they are doing useful 
work.  The run 
latch is typically cleared in the OS idle loop.  Gating by the run latch filters out 
the idle loop.) with a unit mask of 0x00 (No unit mask) count 500000 
Counted PM_RUN_INST_CMPL_GRP1 events ((Group 1 pm_utilization) Number of run 
instructions completed.) with a unit mask of 0x00 (No unit mask) count 500000 

samples  %        samples  %        samples  %      app name        symbol name 
204538    8.3658  32078     1.3965  gcc_base.none   gcc_base.none   reg_is_remote_consta\
nt_p.isra.3.part.4 
124596    5.0961  252227   10.9809  gcc_base.none   gcc_base.none   bitmap_operation 
112326    4.5943  50890     2.2155  libc-2.13.so    libc-2.13.so    memset 
90312     3.6939  21882     0.9527  gcc_base.none   gcc_base.none   compute_transp 
0              0  0              0  vmlinux         vmlinux         .pseries_dedicated\
_idle_sleep 
88894     3.6359  124831    5.4346  gcc_base.none   gcc_base.none   bitmap_element_all\
ocate 
67995     2.7811  41331     1.7994  gcc_base.none   gcc_base.none   ggc_set_mark
56460     2.3093  89484     3.8958  gcc_base.none   gcc_base.none   canon_rtx
54076     2.2118  6965      0.3032  gcc_base.none   gcc_base.none   delete_null_pointer\
_checks
51228     2.0953  26057     1.1344  gcc_base.none   gcc_base.none   ggc_mark_rtx_childr\
en_1 
48057     1.9656  16005     0.6968  gcc_base.none   gcc_base.none   single_set_2 
47160     1.9289  33766     1.4700  gcc_base.none   gcc_base.none   note_stores

Cada evento assistido é representado por um par de colunas na saída. A primeira coluna mostra os números de amostra coletados a partir de um PCM para o evento especificado e a segunda mostra a porcentagem do número total de amostra que ela representa. Conforme visto nesse relatório, o símbolo reg_is_remote_constant_p é o que consome grande parte dos ciclos do processador e é um bom candidato para a otimização do código. Esse perfil identifica somente os símbolos que consomem mais ciclos de CPU, mas não se o pipeline do processador é totalmente utilizado. É possível investigar a utilização do pipeline comparando os resultados dos contadores.

Considere o contador PM_INST_CMPL_GRP1 (o segundo par de colunas); o símbolo bitmap_operation mostra uma maior porcentagem do que o símbolo reg_is_remote_constant_p. Esse contador de desempenho é incrementado para cada instrução de processador concluída, considerando que PM_CYC_GRP1 significa somente o número de ciclos da CPU utilizados. Sem análise posterior, isso pode indicar que o símbolo reg_is_remote_constant_p contém mais paralisações de CPU do que o símbolo bitmap_operation, pois o número de instruções concluídas para o símbolo reg_is_remote_constant_p é significativamente menor. Esse perfil fornece uma sugestão inicial sobre qual símbolo focar esforços de otimização subsequentes.

Antes de começar a se aprofundar e decifrar o código, é aconselhável entender se a carga de trabalho é limitada pela CPU ou pela memória. Isso é importante porque as abordagens de otimização são bastante diferentes para cada tipo de carga de trabalho. Por exemplo, na maioria das vezes, os acessos à memória vêm do cache ou da memória principal (em oposição ao acesso remoto à memória do nó do NUMA) e o desempenho depende quase totalmente dos algoritmos e das estruturas de dados utilizados. Para investigar os padrões de acesso à memória, assista aos dois contadores de desempenho a seguir na Tabela 3:

Tabela 3. Contadores de utilização de memória do POWER7

ContadorDescrição
PM_MEM0_RQ_DISPLeia as solicitações enviadas para a memória principal
PM_MEM0_WQ_DISPSolicitações de gravação enviadas para a memória principal

Esses dois contadores podem indicar se um padrão de acesso à memória é principalmente a partir de leituras de memória, gravações ou ambos. Por meio do uso da mesma referência anterior (403.gcc a partir de SPECcpu2006), o perfil mostra:

Lista 7. Saída a partir do 'opreport -' para a referência 403.gcc (contador PM_MEM0_RQ_DISP e PM_MEM0_WQ_DISP)
CPU: ppc64 POWER7, speed 3550 MHz (estimated) 
Counted PM_MEM0_RQ_DISP_GRP59 events ((Group 59 pm_nest2)  Nest events (MC0/MC1/PB/GX), 
Pair0 Bit1) with a unit mask of 0x00 (No unit mask) count 1000 
Counted PM_MEM0_WQ_DISP_GRP59 events ((Group 59 pm_nest2)  Nest events (MC0/MC1/PB/GX), 
Pair3 Bit1) with a unit mask of 0x00 (No unit mask) count 1000 
samples  %        samples  %        app name                 symbol name 
225841   25.8000  289       0.4086  gcc_base.none            reg_is_remote_constant_p.\
isra.3.part.4 
90068    10.2893  2183      3.0862  gcc_base.none            compute_transp 
54038     6.1733  308       0.4354  gcc_base.none            single_set_2 
32660     3.7311  2006      2.8359  gcc_base.none            delete_null_pointer_checks 
26352     3.0104  1498      2.1178  gcc_base.none            note_stores 
21306     2.4340  1950      2.7568  vmlinux                  .pseries_dedicated_idle_sl\
eep 
18059     2.0631  9186     12.9865  libc-2.13.so             memset 
15867     1.8126  659       0.9316  gcc_base.none            init_alias_analysis

Outro conjunto interessante de contadores de desempenho a ser observado é a pressão de acesso sobre o cache, tanto no L2 quanto no L3. O exemplo a seguir utiliza perf para traçar o perfil do componente SPECcpu2006 483.xalancbmk (consulte Recursos) que é desenvolvido utilizando o GCC do sistema Linux RHEL6.2. Esse componente utiliza as rotinas de alocação de memória de forma profunda, portanto, espera-se muita pressão no subsistema da memória. Para realizar isso, assista aos contadores a seguir na Tabela 4 com o OProfile:

Tabela 4. Contadores de acesso do cache/memória do POWER7

ContadorDescrição
PM_DATA_FROM_L2O Cache de Dados do processador foi recarregado a partir do L2 local devido a uma carga de demanda
PM_DATA_FROM_L3O Cache de Dados do processador foi recarregado a partir do L3 local devido a uma carga de demanda
PM_DATA_FROM_LMEMO Cache de Dados do processador foi recarregado a partir da memória conectada ao mesmo módulo no qual esse processador está localizado
PM_DATA_FROM_RMEMO Cache de Dados do processador foi recarregado a partir da memória conectada a um módulo diferente do qual esse processador está localizado

A saída do perfil mostra:

Lista 8. Saída a partir do 'opreport -' para a referência 489.Xalancbmk (contador PM_DATA_FROM_L2_GRP91 e PM_DATA_FROM_L3_GRP91)
CPU: ppc64 POWER7, speed 3550 MHz (estimated) 
Counted PM_DATA_FROM_L2_GRP91 events ((Group 91 pm_dsource1) The processor's Data Cache
was reloaded from the local L2 due to a demand load.) with a unit mask of 0x00 (No unit
mask) count 1000 
Counted PM_DATA_FROM_L3_GRP91 events ((Group 91 pm_dsource1) The processor's Data Cache
was reloaded from the local L3 due to a demand load.) with a unit mask of 0x00 (No unit
mask) count 1000 
samples  %        samples  %        image name     app name       symbol name 
767827   25.5750  7581      0.2525  gcc_base.none  gcc_base.none  bitmap_element_allocate
377138   12.5618  8341      0.2778  gcc_base.none  gcc_base.none  bitmap_operation 
93334     3.1088  3160      0.1052  gcc_base.none  gcc_base.none  bitmap_bit_p 
70278     2.3408  5913      0.1969  libc-2.13.so   libc-2.13.so   _int_free 
56851     1.8936  22874     0.7618  oprofile       oprofile       /oprofile 
47570     1.5845  2881      0.0959  gcc_base.none  gcc_base.none  rehash_using_reg 
41441     1.3803  8532      0.2841  libc-2.13.so   libc-2.13.so   _int_malloc
Lista 9. Saída a partir do 'opreport -' para a referência 489.Xalancbmk (contador PM_DATA_FROM_LMEM_GRP91 e PM_DATA_FROM_RMEM_GRP91)
Counted PM_DATA_FROM_LMEM_GRP91 events ((Group 91 pm_dsource1) The processor's Data Cache
was reloaded from memory attached to the same module this proccessor is located on.) with
a unit mask of 0x00 (No unit mask) count 1000 
Counted PM_DATA_FROM_RMEM_GRP91 events ((Group 91 pm_dsource1) The processor's Data Cache
was reloaded from memory attached to a different module than this proccessor is located 
on.) with a unit mask of 0x00 (No unit mask) count 1000
samples  %        samples  %        image name     app name       symbol name 
1605      0.3344  0              0  gcc_base.none  gcc_base.none  bitmap_element_allocate
1778      0.3704  0              0  gcc_base.none  gcc_base.none  bitmap_operation 
1231      0.2564  0              0  gcc_base.none  gcc_base.none  bitmap_bit_p 
205       0.0427  0              0  libc-2.13.so   libc-2.13.so   _int_free 
583       0.1215  327      100.000  oprofile       oprofile       /oprofile 
0              0  0              0  gcc_base.none  gcc_base.none  rehash_using_reg 
225       0.0469  0              0  libc-2.13.so   libc-2.13.so   _int_malloc

A interpretação da saída do perfil mostra que grande parte da pressão do cache vem do acesso do L2 com quase nenhuma recarga de L3 de demanda, já que o valor de amostra do contador relativo e total para o acesso do L2 (PM_DATA_FROM_L2) é bem maior do que a recarga de demanda do L3 (PM_DATA_FROM_L3). É possível obter mais informações, como se o acesso do L2 está fazendo com que a CPU paralise devido às perdas de acerto no cache, somente com uma análise mais abrangente (assistindo alguns contadores). Um conclusão que pode ser tirada desse perfil de exemplo é que o acesso à memória principal (evento PM_DATA_FROM_LMEM ) é bastante baixo comparado ao acesso ao cache e não há nenhum acesso remoto (evento PM_DATA_FROM_RMEM), indicando nenhum acesso remoto à memória do nó do NUMA. A análise de pontos de acesso e os padrões de acesso à memória podem orientar os esforços de otimização; nesse caso, é necessária uma análise mais profunda para identificar o que realmente faz com a CPU paralise, pois a simples identificação dos pontos de acesso da carga de trabalho e do padrão de acesso à memória não é suficiente para identificar as paralisações de CPU de forma correta.

Para criar melhores estratégias para uma análise mais profunda da otimização do desempenho, será necessário utilizar a ferramenta perf , em vez de OProfile , pois diversos contadores do POWER7 CBM precisam ser assistidos simultaneamente, os 22 apresentados na Figura 3 além de criar melhores estratégias para a otimização do desempenho. Muitos desses eventos estão em diferentes grupos, isto é, o uso de OProfile requer muitas execuções da mesma carga de trabalho. A ferramenta perf multiplexará a observação dos contadores de hardware quando os contadores especificados estiverem em mais de um grupo. Embora isso gere um resultado menos preciso, o resultado geral tende a ser bastante semelhante ao esperado com a vantagem do menor tempo gasto na criação de perfil.

O exemplo a seguir utiliza perf para traçar o perfil do mesmo componente SPECcpu2006 483.xalancbmk. Para traçar o perfil desse componente, emita o comando na Lista 10:

Lista 10. Comando perf para o POWER7 CBM gerado
$ /usr/bin/perf stat -C 0 -e r100f2,r4001a,r100f8,r4001c,r2001a,r200f4,r2004a,r4004a,
r4004e,r4004c,r20016,r40018,r20012,r40016,r40012,r20018,r4000a,r2001c,r1001c,r20014,
r40014,r30004 taskset -c 0 ./Xalan_base.none -v t5.xml xalanc.xsl > power7_cbm.dat

Esse comando fará com que perf assista os eventos brutos definidos pelo argumento -e na CPU especificada por -c. A chamada do conjunto de tarefas assegura que o componente retornará exclusivamente no número 0 da CPU. A carga de trabalho ./Xalan_base.none -v t5.xml xalanc.xsl pode ser substituída por outro aplicativo para ser traçado o perfil. Após a conclusão do perfil, o comando perf irá gerar uma tabela simples da contagem total para cada evento bruto com o número total de segundos decorridos:

Lista 11. Saída a partir do 'perf stat' para a referência 489.Xalancbmk
Performance counter stats for 'taskset -c 0 ./Xalan_base.none -v t5.xml xalanc.xsl': 


   366,860,486,404 r100f2                                                       [18.15%] 
     8,090,500,758 r4001a                                                       [13.65%] 
    50,655,176,004 r100f8                                                       [ 9.13%] 
    11,358,043,420 r4001c                                                       [ 9.11%] 
    10,318,533,758 r2001a                                                       [13.68%] 
 1,301,183,175,870 r200f4                                                       [18.22%] 
     2,150,935,303 r2004a                                                       [ 9.10%] 
                 0 r4004a                                                       [13.65%] 
   211,224,577,427 r4004e                                                       [ 4.54%] 
   212,033,138,844 r4004c                                                       [ 4.54%] 
   264,721,636,705 r20016                                                       [ 9.09%] 
    22,176,093,590 r40018                                                       [ 9.11%] 
   510,728,741,936 r20012                                                       [ 9.10%] 
    39,823,575,049 r40016                                                       [ 9.07%] 
     7,219,335,816 r40012                                                       [ 4.54%] 
         1,585,358 r20018                                                       [ 9.08%] 
   882,639,601,431 r4000a                                                       [ 9.08%] 
     1,219,039,175 r2001c                                                       [ 9.08%] 
         3,107,304 r1001c                                                       [13.62%] 
   120,319,547,023 r20014                                                       [ 9.09%] 
    50,684,413,751 r40014                                                       [13.62%] 
   366,940,826,307 r30004                                                       [18.16%] 

     461.057870036 seconds time elapsed

Para analisar a saída de perf com relação ao POWER7 CBM, um script Python é fornecido (verifique o power7_cbm.zip em Downloads), que compõe a métrica do contador a partir dos contadores virtuais e de hardware coletados. Para criar um relatório, emita o comando na Lista 12:

Lista 12. Chamada do script python do POWER7 CBM
$ power7_cbm.py power7_cbm.dat

A saída semelhante à Lista 13 será impressa:

Lista 13. Saída a partir do 'power7_cbm.py' para a referência 489.Xalancbmk
CPI Breakdown Model (Concluído)

Métrica                        :            Valor :    Porcentagem
PM_CMPLU_STALL_DIV             :    49802421337,0 :        0,0
PM_CMPLU_STALL_FXU_OTHER       :    67578558649,0 :        5,2
PM_CMPLU_STALL_SCALAR_LONG     :        2011413,0 :        0,0
PM_CMPLU_STALL_SCALAR_OTHER    :     7195240404,0 :        0,6
PM_CMPLU_STALL_VECTOR_LONG     :              0,0 :        0,0
PM_CMPLU_STALL_VECTOR_OTHER    :     1209603592,0 :        0,1
PM_CMPLU_STALL_ERAT_MISS       :    22193968056,0 :        1,7
PM_CMPLU_STALL_REJECT_OTHER    :    18190293594,0 :        1,4
PM_CMPLU_STALL_DCACHE_MISS     :   261865838255,0 :       20,3
PM_CMPLU_STALL_STORE           :     2001544985,0 :        0,2
PM_CMPLU_STALL_LSU_OTHER       :   202313206181,0 :       15,7
PM_CMPLU_STALL_THRD            :        2025705,0 :        0,0
PM_CMPLU_STALL_BRU             :   208356542821,0 :       16,2
PM_CMPLU_STALL_IFU_OTHER       :     2171796336,0 :        0,2
PM_CMPLU_STALL_OTHER           :    30895294057,0 :        2,4
PM_GCT_NOSLOT_IC_MISS          :     9805421042,0 :        0,8
PM_GCT_NOSLOT_BR_MPRED         :     7823508357,0 :        0,6
PM_GCT_NOSLOT_BR_MPRED_IC_MISS :    11059314150,0 :        0,9
PM_GCT_EMPTY_OTHER             :    20292049774,0 :        1,6
PM_1PLUS_PPC_CMPL              :   365158978504,0 :       28,3
OVERHEAD_EXPANSION             :      590057044,0 :        0,0
Total                                             :       96,1

Esse relatório é baseado nos valores estatísticos dentro de uma margem de erro, portanto, as porcentagens finais não são totalmente precisas. Mesmo com uma margem de erro grande, cerca de 20% do total de paralisações de CPU se devem às perdas de acerto no cache de dados (PM_CMPLU_STALL_DCACHE_MISS). A porcentagem de conclusão da instrução final (PM_1PLUS_PPC_CMPL) é cerca de 28%.

Otimizações futuras devem tentar maximizar esse número diminuindo as paralisações da CPU e/ou as porcentagens de GCT (Global Completion Table). Com base nesse relatório, outro caminho para análise é identificar o código no qual as paralisações estão ocorrendo. Isso pode ser realizado utilizando o comando perf record . Ele rastreará o desempenho de um contador bruto e criará um mapa com um registro de chamadas do processo, permitindo a identificação de qual símbolo gerou mais eventos de hardware. Esse processo é semelhante ao modo como o OProfile funciona. Nesse exemplo, para rastrear os eventos PM_CMPLU_STALL_DCACHE_MISS , emita o comando na Lista 14:

Lista 14. perf record para o eventoPM_CMPLU_STALL_DCACHE_MISS
$ /usr/bin/perf record -C 0 -e r20016 taskset -c 0 ./Xalan_base.none -v t5.xml xalanc.xsl

O comando perf criará um arquivo de dados (geralmente "perf.dat") com os resultados. Ele pode ser lido de forma interativa utilizando o comando perf report como na Lista 15:

Lista 15. Saída a partir do 'perf report' para a referência 489.Xalancbmk
Eventos: 192  bruto 0x20016
    39,58%  Xalan_base.none  Xalan_base.none  [.] xercesc_2_5::ValueStore::contains
    11,46%  Xalan_base.none  Xalan_base.none  [.] xalanc_1_8::XStringCachedAllocator
     9,90%  Xalan_base.none  Xalan_base.none  [.] xalanc_1_8::XStringCachedAllocator
     7,29%  Xalan_base.none  Xalan_base.none  [.] xercesc_2_5::ValueStore::isDuplica
     5,21%  Xalan_base.none  libc-2.13.so     [.] _int_malloc
     5,21%  Xalan_base.none  Xalan_base.none  [.] __gnu_cxx::__normal_iterator<xa
     4,17%  Xalan_base.none  libc-2.13.so     [.] __GI___libc_malloc
     2,08%  Xalan_base.none  libc-2.13.so     [.] malloc_consolidate.part.4
     1,56%  Xalan_base.none  Xalan_base.none  [.] xalanc_1_8::ReusableArenaBlock<xa
     1,56%  Xalan_base.none  Xalan_base.none  [.] xalanc_1_8::ReusableArenaBlock<xa
     1,04%  Xalan_base.none  libc-2.13.so     [.] __free
[...]

Com essa análise, utilizando o contador do POWER7 CBM e a ferramenta perf report , seu esforço de otimização pode se concentrar na otimização da memória e do acesso ao cache no símbolo xercesc_2_5::ValueStore::contains(xercesc_2_5::FieldValueMap const*).

Esse exemplo é apenas um subconjunto de possível análise. O POWER7 CBM mostra que, embora as paralisações do cache de dados aparecem como a maior causa de paralisações de CPU, a unidade de carga e de armazenamento (PM_CMPLU_STALL_LSU) e a unidade de ramificação (PM_CMPLU_STALL_BRU) são origens de paralisações. Uma análise mais profunda pode abordar esses contadores.

Caso de Referência

O caso de referência a seguir aplica essas estratégias de avaliação de desempenho para analisar uma implementação de função matemática trigonométrica. Com base nos resultados da análise, as oportunidades de otimização serão identificadas. A função utilizada nesse caso de referência é hypot , definida como o comprimento da hipotenusa de um triângulo reto. A função é definida pelo C99, POSIX.1-2001 como:

double hypot(double x, double y);
A função hypot() retorna sqrt(x*x+y*y). Com êxito, essa função retorna o comprimento de um triângulo de ângulo reto com lados de comprimento x e y. Se x ou y for um infinito, um infinito positivo é retornado. Se x ou y for um NaN e o outro argumento não for um infinito, um NaN é retornado. Se o resultado estourar, ocorre uma variedade de erros e as funções retornam HUGE_VAL, HUGE_VALF ou HUGE_VALL, respectivamente. Se os dois argumentos forem subnormais e o resultado for subnormal, ocorre uma variedade de erros e o resultado correto é retornado.

Embora o algoritmo pareça simples, a manipulação do argumento Vírgula Flutuante (FP) de Infinito e NaN e o estouro/estouro negativo relacionado às operações da FP impõem algum desafio com impactos no desempenho. A biblioteca GNU C (consulte Recursos) fornece uma implementação de hypot , localizado na árvore de origem em sysdeps/ieee754/dbl-64/e_hypot.c:

Observação: As informações sobre licença para essa amostra de código estão incluídas no Anexo.

Lista 16. Código de origem hypot padrão da GLIBC
double __ieee754_hypot(double x, double y) 
{ 
        double a,b,t1,t2,y1,y2,w; 
        int32_t j,k,ha,hb; 

        GET_HIGH_WORD(ha,x); 
        ha &= 0x7fffffff; 
        GET_HIGH_WORD(hb,y); 
        hb &= 0x7fffffff; 
        if(hb > ha) {a=y;b=x;j=ha; ha=hb;hb=j;} else {a=x;b=y;} 
        SET_HIGH_WORD(a,ha);    /* a <- |a| */ 
        SET_HIGH_WORD(b,hb);    /* b <- |b| */ 
        if((ha-hb)>0x3c00000) {return a+b;} /* x/y > 2**60 */ 
        k=0; 
        if(ha > 0x5f300000) {   /* a>2**500 */ 
           if(ha >= 0x7ff00000) {       /* Inf or NaN */ 
               u_int32_t low; 
               w = a+b;                 /* for sNaN */ 
               GET_LOW_WORD(low,a); 
               if(((ha&0xfffff)|low)==0) w = a; 
               GET_LOW_WORD(low,b); 
               if(((hb^0x7ff00000)|low)==0) w = b; 
               return w; 
           } 
           /* scale a and b by 2**-600 */ 
           ha -= 0x25800000; hb -= 0x25800000;  k += 600; 
           SET_HIGH_WORD(a,ha); 
           SET_HIGH_WORD(b,hb); 
        } 
        if(hb < 0x20b00000) {   /* b < 2**-500 */ 
            if(hb <= 0x000fffff) {      /* subnormal b or 0 */ 
                u_int32_t low; 
                GET_LOW_WORD(low,b); 
                if((hb|low)==0) return a; 
                t1=0; 
                SET_HIGH_WORD(t1,0x7fd00000);   /* t1=2^1022 */ 
                b *= t1; 
                a *= t1; 
                k -= 1022; 
            } else {            /* scale a and b by 2^600 */ 
                ha += 0x25800000;       /* a *= 2^600 */ 
                hb += 0x25800000;       /* b *= 2^600 */ 
                k -= 600; 
                SET_HIGH_WORD(a,ha); 
                SET_HIGH_WORD(b,hb); 
            } 
        } 
    /* medium size a and b */ 
        w = a-b; 
        if (w>b) { 
            t1 = 0; 
            SET_HIGH_WORD(t1,ha); 
            t2 = a-t1; 
            w  = __ieee754_sqrt(t1*t1-(b*(-b)-t2*(a+t1))); 
        } else { 
            a  = a+a; 
            y1 = 0; 
            SET_HIGH_WORD(y1,hb); 
            y2 = b - y1; 
            t1 = 0; 
            SET_HIGH_WORD(t1,ha+0x00100000); 
            t2 = a - t1; 
            w  = __ieee754_sqrt(t1*y1-(w*(-w)-(t1*y2+t2*b))); 
        } 
        if(k!=0) { 
            u_int32_t high; 
            t1 = 1.0; 
            GET_HIGH_WORD(high,t1); 
            SET_HIGH_WORD(t1,high+(k<<20)); 
            return t1*w; 
        } else return w; 
}

Essa implementação é bastante complexa, principalmente porque o algoritmo executa muitas conversões FP para INT bit por bit. Ela assume que determinadas operações FP, como comparações e multiplicações, são mais caras ao usar instruções de vírgula flutuante do que ao usar instruções de ponto fixo. Isso é verdade em algumas arquiteturas, mas não na Arquitetura Power.

Sua primeira etapa é avaliar se essa implementação é criar uma referência cujo perfil pode ser traçado. Nesse caso, como é simplesmente uma função com dois argumentos e um algoritmo simples (sem chamadas internas ou caminhos adicionais), uma referência simples pode ser criada para avaliá-la (verifique hypot_bench.tar.gz em Downloads). A referência é parte da avaliação de desempenho; as otimizações devem agilizar os algoritmos ou suas partes fundamentais que aproveitam o desempenho total da carga de trabalho. Referências sintéticas, como essa, devem representar a utilização normal dessa função. Como os esforços de otimização tendem a consumir recursos e tempo, é preciso focar nos casos de uso mais comuns ou no comportamento esperado. A tentativa de otimizar o código que representa baixo uso total do programa tende a ser um desperdício de recursos.

Como essa é uma análise de desempenho em uma única função, é possível ignorar a análise do ponto de acesso e focar na análise do CBM. Utilizando a referência em hypot_bench.c juntamente com perf, as informações do CBM na Lista 17:

Lista 17. Saída a partir do 'power7_cbm.py' para a referência hypot
CPI Breakdown Model (Concluído)

Métrica                        :            Valor :    Porcentagem
PM_CMPLU_STALL_DIV             :        8921688,0 :        8,7
PM_CMPLU_STALL_FXU_OTHER       :    13953382275,0 :        5,0
PM_CMPLU_STALL_SCALAR_LONG     :    24380128688,0 :        8,7
PM_CMPLU_STALL_SCALAR_OTHER    :    33862492798,0 :       12,0
PM_CMPLU_STALL_VECTOR_LONG     :              0,0 :        0,0
PM_CMPLU_STALL_VECTOR_OTHER    :      275057010,0 :        0,1
PM_CMPLU_STALL_ERAT_MISS       :         173439,0 :        0,0
PM_CMPLU_STALL_REJECT_OTHER    :         902838,0 :        0,0
PM_CMPLU_STALL_DCACHE_MISS     :       15200163,0 :        0,0
PM_CMPLU_STALL_STORE           :        1837414,0 :        0,0
PM_CMPLU_STALL_LSU_OTHER       :    94866270200,0 :       33,7
PM_CMPLU_STALL_THRD            :         569036,0 :        0,0
PM_CMPLU_STALL_BRU             :    10470012464,0 :        3,7
PM_CMPLU_STALL_IFU_OTHER       :      -73357562,0 :        0,0
PM_CMPLU_STALL_OTHER           :     7140295432,0 :        2,5
PM_GCT_NOSLOT_IC_MISS          :        3586554,0 :        0,0
PM_GCT_NOSLOT_BR_MPRED         :     1008950510,0 :        0,4
PM_GCT_NOSLOT_BR_MPRED_IC_MISS :         795943,0 :        0,0
PM_GCT_EMPTY_OTHER             :    42488384303,0 :       15,1
PM_1PLUS_PPC_CMPL              :    53138626513,0 :       18,9
OVERHEAD_EXPANSION             :       30852715,0 :        0,0
Total                                             :      108,7

A análise do perfil mostra que grande parte das paralisações de CPU e, portanto, da perda de desempenho, vem da Load and Store Unit (LSU - contador PM_CMPLU_STALL_LSU_OTHER). A LSU possui vários contadores associados, no entanto, durante a análise de paralisação de CPU, seu foco é os contadores associados às degradações de desempenho. Os que apresentam degradações de desempenho no POWER são associados aos perigos de Load-Hit-Store (LHS). Essa é uma grande paralisação que ocorre quando a CPU grava dados em um endereço e, em seguida, tenta carregar esses dados novamente logo em seguida. A próxima etapa é verificar se isso está acontecendo nesse algoritmo específico verificando, primeiramente, o evento PM_LSU_REJECT_LHS (código bruto "rc8ac"), conforme mostrado na Lista 18.

Lista 18. perf record do evento PM_LSU_REJECT_LHS POWER7
$ perf record -C 0 -e rc8ac taskset -c 0 ./hypot_bench_glibc
$ perf report
Events: 14K raw 0xc8ac
    79.19%  hypot_bench_gli  libm-2.12.so       [.] __ieee754_hypot
    10.38%  hypot_bench_gli  libm-2.12.so       [.] __hypot
     6.34%  hypot_bench_gli  libm-2.12.so       [.] __GI___finite

A saída do perfil mostra que o símbolo __ieee754_hypot está gerando grande parte dos eventos PM_LSU_REJECT_LHS . Investigando o código do conjunto gerado pelo compilador para identificar quais instruções estão gerando o evento. Expanda o símbolo __ieee754_hypot para anotar o conjunto iterando na tela do perf report e selecionando o símbolo __ieee754_hypot , que mostra a saída na Lista 19.

Lista 19. perf report do evento PM_LSU_REJECT_LHS POWER7
         :        00000080fc38b730 <.__ieee754_hypot>:
    0.00 :          80fc38b730:   7c 08 02 a6     mflr    r0
    0.00 :          80fc38b734:   fb c1 ff f0     std     r30,-16(r1)
    0.00 :          80fc38b738:   fb e1 ff f8     std     r31,-8(r1)
   13.62 :          80fc38b73c:   f8 01 00 10     std     r0,16(r1)
    0.00 :          80fc38b740:   f8 21 ff 71     stdu    r1,-144(r1)
   10.82 :          80fc38b744:   d8 21 00 70     stfd    f1,112(r1)
    0.23 :          80fc38b748:   e9 21 00 70     ld      r9,112(r1)
   17.54 :          80fc38b74c:   d8 41 00 70     stfd    f2,112(r1)
    0.00 :          80fc38b750:   79 29 00 62     rldicl  r9,r9,32,33
    0.00 :          80fc38b754:   e9 61 00 70     ld      r11,112(r1)
    0.00 :          80fc38b758:   e8 01 00 70     ld      r0,112(r1)
    8.46 :          80fc38b75c:   d8 21 00 70     stfd    f1,112(r1)
[...]

No início do código, a implementação utiliza a macro GET_HIGH_WORD para transformar um float em um integer para operações bit a bit posteriores. math/math_private.h da GLIBC define a macro utilizando o código na Lista 20.

Lista 20. Definição da macro GET_HIGH_WORD
#define GET_HIGH_WORD(i,d)                                      \
do {                                                            \
  ieee_double_shape_type gh_u;                                  \
  gh_u.value = (d);                                             \
  (i) = gh_u.parts.msw;                                         \
} while (0)

Um possível culpado por uma paralisação de LHS nessa macro é a operação que lê as atribuições de valor flutuante para interno e, em seguida, as lê na variável i. O processador POWER7 não tem uma instrução nativa para mover o conteúdo de um registro de vírgula flutuante, bit a bit, para um registro de ponto fixo. Isso é realizado no POWER armazenando o número de FP no registro de vírgula flutuante para a memória utilizando uma operação de armazenamento para, em seguida, carregar a mesma localização da memória em um ponto fixo (fins gerais). Como o acesso à memória é mais lento do que as operações de registro (mesmo ao acessar o cache de dados L1), a CPU é instalada durante o armazenamento para concluir a carga subsequente.

Observação: O documento, "POWER ISA 2.06 (POWER7)" (consulte Recursos), contém mais informações.

Frequentemente, eventos do contador de desempenho acionam interrupções que salvam o endereço de um PC de uma instrução próxima às instruções de execução. Isso pode levar à anotação do conjunto, que não é totalmente precisa. Para mitigar esse comportamento, o POWER4 e versões mais recentes têm um conjunto limitado de contadores de desempenho denominado marked. Instruções marcadas irão gerar menos eventos por intervalo de tempo; no entanto, a instrução do PC será exata, resultando em uma anotação de conjunto precisa. Eventos marcados têm o prefixo PM_MRK na lista do contador OProfile obtida por opcontrol -l.

Para confirmar a análise, assista ao contador PM_MRK_LSU_REJECT_LHS . Os dois contadores, PM_MRK_LSU_REJECT_LHS e PM_LSU_REJECT_LHS, assistem ao mesmo evento de desempenho. No entanto, o contador marcado (PM_MRK_LSU_REJECT_LHS) irá gerar menos eventos por intervalo de tempo, mas com uma anotação de conjunto mais precisa. (Consulte a Lista 21.)

Lista 21. perf record do evento PM_MRK_LSU_REJECT_LHS do POWER7
$ perf record -C 0 -e rd082 taskset -c 0 ./hypot_bench_glibc
$ perf report
Events: 256K raw 0xd082
    64.61%  hypot_bench_gli  libm-2.12.so       [.] __ieee754_hypot
    35.33%  hypot_bench_gli  libm-2.12.so       [.] __GI___finite

Isso gera a anotação de conjunto na Lista 22..

Lista 22. perf report do evento PM_MRK_LSU_REJECT_LHS do POWER7
         :        00000080fc38b730 <.__ieee754_hypot>:
[...]
    1.23 :          80fc38b7a8:   c9 a1 00 70     lfd     f13,112(r1)
    0.00 :          80fc38b7ac:   f8 01 00 70     std     r0,112(r1)
   32.66 :          80fc38b7b0:   c8 01 00 70     lfd     f0,112(r1)
[...]
    0.00 :          80fc38b954:   f8 01 00 70     std     r0,112(r1)
    0.00 :          80fc38b958:   e8 0b 00 00     ld      r0,0(r11)
    0.00 :          80fc38b95c:   79 00 00 0e     rldimi  r0,r8,32,0
   61.72 :          80fc38b960:   c9 61 00 70     lfd     f11,112(r1
[...]

Outro símbolo mostra cerca de 35% dos eventos gerados com comportamento semelhante na Lista 23.

Lista 23. Mais destaques do perf report
         :        00000080fc3a2610 <.__finitel>>
    0.00 :          80fc3a2610:   d8 21 ff f0     stfd    f1,-16(r1)
  100.00 :          80fc3a2614:   e8 01 ff f0     ld      r0,-16(r1)

Com base nessas informações, seu esforço de otimização pode ser eliminar essas paralisações removendo as conversões FP para INT. O processador POWER tem uma unidade de execução de Vírgula Flutuante rápida e eficiente, portanto, não há necessidade de executar esses cálculos com instruções de Ponto Fixo. O algoritmo utilizado pelo POWER atualmente na GLIBC (sysdeps/powerpc/fpu/e_hypot.c) removeu todas as paralisações de LHS utilizando somente operações de FP. O resultado é o algoritmo bem mais simples, na Lista 24.

Lista 24. Código fonte hypot da GLIBC do PowerPC
double
__ieee754_hypot (double x, double y)
{
  x = fabs (x);
  y = fabs (y);

  TEST_INF_NAN (x, y);

  if (y > x)
    {
      double t = x;
      x = y;
      y = t;
    }
  if (y == 0.0 || (x / y) > two60)
    {
      return x + y;
    }
  if (x > two500)
    {
      x *= twoM600;
      y *= twoM600;
      return __ieee754_sqrt (x * x + y * y) / twoM600;
    }
  if (y < twoM500)
    {
      if (y <= pdnum)
        {
          x *= two1022;
          y *= two1022;
          return __ieee754_sqrt (x * x + y * y) / two1022;
        }
      else
        {
          x *= two600;
          y *= two600;
          return __ieee754_sqrt (x * x + y * y) / two600;
        }
    }
  return __ieee754_sqrt (x * x + y * y);
}

A macro TEST_INF_NAN é uma otimização ainda menor que testa se um número é NaN ou INFINITY antes de começar outras operações FP (isso se deve ao fato de que as operações em NaN e INFINITY podem gerar exceções e a especificação de função não permite isso). No POWER7, as chamadas das funções isinf e isnan são otimizadas pelo compilador para instruções FP e não geram chamadas de função extra, enquanto que, em processadores mais antigos (POWER6 e anteriores), elas irão gerar uma chamada para as respectivas funções. A otimização é basicamente a mesma implementação, mas sequencial, a fim de evitar chamadas de função.

Finalmente, para comparar as duas implementações, execute o teste simples a seguir. Recompile a GLIBC com e sem o novo algoritmo e compare o tempo total para cada execução de referência. Os resultados de implementação padrão da GLIBC encontram-se na Lista 25:

Lista 25. Referência com hypot padrão da GLIBC
$ /usr/bin/time ./hypot_bench_glibc
INF_CASE       : elapsed time: 14:994339 
NAN_CASE       : elapsed time: 14:707085 
TWO60_CASE     : elapsed time: 12:983906 
TWO500_CASE    : elapsed time: 10:589746 
TWOM500_CASE   : elapsed time: 11:215079 
NORMAL_CASE    : elapsed time: 15:325237 
79.80user 0.01system 1:19.81elapsed 99%CPU (0avgtext+0avgdata 151552maxresident)k 
0inputs+0outputs (0major+48minor)pagefaults 0swaps

Os resultados da versão otimizada encontram-se na Lista 26:

Lista 26. Referência com hypot otimizado da GLIBC
$ /usr/bin/time ./hypot_bench_glibc 
INF_CASE       : elapsed time: 4:667043 
NAN_CASE       : elapsed time: 5:100940 
TWO60_CASE     : elapsed time: 6:245313 
TWO500_CASE    : elapsed time: 4:838627 
TWOM500_CASE   : elapsed time: 8:946053 
NORMAL_CASE    : elapsed time: 6:245218 
36.03user 0.00system 0:36.04elapsed 99%CPU (0avgtext+0avgdata 163840maxresident)k 
0inputs+0outputs (0major+50minor)pagefaults 0swaps

Essa é uma melhoria de desempenho final de mais de 100%, reduzindo o tempo de referência pela metade.

Conclusão

A avaliação de desempenho com criação de perfil de contador de hardware é uma ferramenta eficiente para entender como uma carga de trabalho se comporta em um determinado processador e oferecer sugestões de onde trabalhar nas otimizações de desempenho. O processador POWER7 mais recente possui centenas de contadores de desempenho disponíveis, portanto, apresentamos um modelo simples de como mapear a carga de trabalho para as paralisações de CPU. Entender se o POWER7 CBM é um pouco complicado, portanto, também explicamos as ferramentas para o Linux que simplificam isso. Estratégias para a avaliação de desempenho, com ênfase em como encontrar pontos de acesso, como entender o padrão de memória de um aplicativo e como utilizar o POWER7 CBM. Finalmente, utilizamos uma otimização recente feita em uma função trigonométrica em uma GLIBC para explicar a análise de desempenho utilizada para resultar no código otimizado.

Anexo

É concedida permissão para copiar, distribuir e/ou modificar este documento nos termos da GNU Free Documentation License, Versão 1.3; sem Seções Invariáveis, sem Textos na Capa Frontal e sem Textos na Contra-Capa. Uma cópia desta licença está disponível em http://www.gnu.org/licenses/fdl-1.3.txt.

Downloads

DescriçãoNomeTamanhoMétodo de download
GLIBC hypot benchmarkhypot_bench.tar.gz6KBHTTP
Python script to format perf outputpower7_cbm.zip2KBHTTP

Informações sobre métodos de download

Recursos

Aprender

Obter produtos e tecnologias

  • Saiba mais sobre OProfile no website do projeto.
  • Saiba mais sobre a ferramenta perf—o código é mantido com a origem do kernel do Linux.
  • Encontre códigos brutos do POWER7 para perf no site do projeto libpfm4 .
  • Consulte a GNU C Library na página do projeto.
  • Explore o Advance Toolchain for Linux on POWER , uma coleção de produtos de desenvolvimento de aplicativos de softwares gratuitos especificamente customizados para um uso inicial dos recursos de hardware da IBM.
  • Avalie produtos IBM da maneira que for melhor para você: faça download da versão de teste de um produto, avalie um produto online, use-o em um ambiente de nuvem ou passe algumas horas na SOA Sandbox aprendendo a implementar Arquitetura Orientada a Serviços de modo eficiente.

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Linux, Software livre
ArticleID=823050
ArticleTitle=Avaliar o desempenho para Linux on POWER
publish-date=05162014