Ciclos Por Instrução: Análise de Performance em Power7

Explore a análise de CPI (Ciclos por Instrução) e utilize as ferramentas de performance para identificar gargalos nos programas e no sistema. Os eventos e contadores de performance são usados para monitorar o comportamento de um workload rodando em Power7.

Rafael Folco, Software Engineer, IBM

Rafael FolcoRafael é engenheiro de software no Linux Technology Center há mais de 6 anos e atua nas áreas de Power, PowerVM, Performance, BigData e HPC. Ele tem 10 anos de experiência em desenvolvimento e qualidade de software e é autor de 4 Redbooks. DevWorks ID: rfolco. Perfil My developerWorks



17/Dez/2012

O real desempenho de um workload pode ser mensurado em ciclos por instrução - Cycles Per Instruction (CPI) – através de contadores de performace. A contagem de eventos do processador permite analisar a performance de aplicações, compiladores e também do próprio hardware. Este artigo apresenta os contadores de performance na plataforma Power7 e suas respectivas funções, e mostra como coletar e analisar essas informações no Linux através de ferramentas de performance como oprofile e perf.


Contadores de Performance em POWER7

A arquitetura do microprocessador POWER7 fornece unidades de monitoramento de performance – Performance Monitor Units (PMUs) - para registro de eventos de performance nos registradores. Em POWER7 existem diversos contadores programáveis disponíveis para contar eventos que podem calcular os componentes de CPI e determinar como melhorar a performance de um workload. Desta forma, você pode monitorar eventos sensíveis de performance específicos quebrando o CPI do seu workload em componentes individuais baseado no que o pipeline está fazendo. Esta abordagem é conhecida como análise detalhada de CPI, ou em inglês, “CPI breakdown analysis”.

CPI – Ciclos Por Instrução - é a métrica ideal para caracterizar a performance de um workload em arquiteturas modernas Multi-Threading. O termo “Ciclos por Instrução” é definido como o número de ciclos de clock do processador necessários para completar uma instrução. CPI é uma quantidade relativa e de forma simplificada é representada pela seguinte fórmula:

CPI = Total de ciclos / instruções completas

Um valor de CPI alto significa baixa utilização dos recursos da máquina. Neste contexto de desempenho o que medimos é o “throughput” da máquina, ou seja, quantas instruções completas o hardware é capaz de entregar em uma fração de tempo. Fica claro que esta métrica de desempenho não se trata de quão rápido o hardware completa uma instrução, mas quantas operações ele é capaz de processar em paralelo e isto implica em quão ocupado está o hardware.

A análise de CPI permite caracterizar workloads, monitorar o pipeline de execução, e identificar problemas de performance nas aplicações, compiladores e no próprio hardware. Este trabalho requer um conhecimento mais profundo dos eventos do pipeline e das ferramentas de performance para processar as informações.

A arquitetura POWER7 permite despachar grupos de instruções a cada ciclo e as instruções são executadas fora de ordem no pipeline de excução. Os estágios do pipeline são:

  1. BUSCA
    As instruções são buscadas do cache de instrução.
  2. DESPACHO
    As instruções são colocadas em grupos de até 6 instruções e distribuídas para as filas de emissão. Uma entrada na tabela GCT – Global Completion Table – é criada para rastrear cada grupo que foi despachado e está em execução no pipeline.
  3. EMISSÃO
    Até 8 instruções por vez são movidas das filas de emissão para as unidades funcionais de destino.
  4. EXECUÇÃO
    Instruções despachadas em ordem são executadas e terminadas fora de ordem em qualquer unidade funcional.
  5. TÉRMINO
    Um grupo de instruções é marcado como completado quando todas as suas instruções terminam de executar. O grupo completo é desalocado da tabela GCT.

A análise de CPI é dividida em três grandes classes de ciclos:

  • Grupo de instruções completo
  • Tabela GCT vazia
  • Grupo(s) presente(s) na tabela GCT, mas nenhum completo (completion stall cycles)

A tabela a seguir contém as possíveis categorias de eventos que podem ocorrer em um ciclo de pipeline relevantes à análise de CPI:

Tabela 1: Classificação dos ciclos no pipeline de execução

Os eventos de ciclos paralisados (completion stall cycles) acontecem em qualquer intervalo de tempo em que nenhum grupo de instruções completou a execução. Esses eventos contém o prefixo _STALL na tabela a seguir.

EventoDescrição
PM_1PLUS_PPC_CMPL1 ou mais instruções completadas
PM_CMPLU_STALLNenhum grupo completo (tabela GCT não vazia)
PM_CMPLU_STALL_BRUA última instrução completada foi na BRU – Branch Unit
PM_CMPLU_STALL_DCACHE_MISSA última instrução completada sofreu uma falha na cache de dados
PM_CMPLU_STALL_DFUA última instrução completada foi na DFU – Decimal Floating Point Unit
PM_CMPLU_STALL_DIVA última instrução completada foi uma divisão de ponto fixo (número fixo de casas decimais) – FXUFixed Point Unit
PM_CMPLU_STALL_ERAT_MISSA última instrução completada sofreu uma falha de mapeamento de endereço (ERAT - Effective-to-Real Address Translation)
PM_CMPLU_STALL_FXUA última instrução completada foi na unidade de ponto fixo - FXU
PM_CMPLU_STALL_IFUA última instrução completada foi na unidade de busca – IFU – Instruction Fetch Unit
PM_CMPLU_STALL_LSUA última instrução completada foi na unidade de Load/Store – LSULoad Store Unit
PM_CMPLU_STALL_REJECTA última instrução completada sofreu uma rejeição de Load/Store
PM_CMPLU_STALL_SCALARA última instrução completada foi de ponto flutuante escalar
PM_CMPLU_STALL_SCALAR_LONGA última instrução completada foi uma divisão de ponto flutuante ou raiz quadrada
PM_CMPLU_STALL_STOREA última instrução completada foi um Store.
PM_CMPLU_STALL_THRDConflito de threads. Grupo de instruções pronto pra completar, porém na vez de outro thread.
PM_CMPLU_STALL_VECTORA última instrução completada foi vetorial.
M_CMPLU_STALL_VECTOR_LONGA última instrução completada foi de vetor de longa latência.
PM_GCT_NOSLOT_BR_MPREDTabela GCT vazia devido à falha de predição da instrução
PM_GCT_NOSLOT_BR_MPRED_IC_MISSTabela GCT vazia devido à falha de predição da instrução e falha da cache de instrução
PM_GCT_NOSLOT_CYCA tabela GCT não tem nenhuma entrada deste thread
PM_GCT_NOSLOT_IC_MISSTabela GCT vazia devido à falha da cache de instrução
PM_GRP_CMPLExecução do grupo de instruções completo
PM_RUN_CYCCiclos de processamento em que sistema não está ocioso
PM_RUN_INST_CMPLNúmero de instruções completadas

Tabela 2: Lista de eventos relevantes à análise de CPI

Ciclos base completados
BASE_COMPLETION_CPI = PM_1PLUS_PPC_CMPL / PM_RUN_INST_CMPL

Ciclos nos quais um grupo completou
COMPLETION_CPI = PM_GRP_CMPL / PM_RUN_INST_CMPL

Ciclos devido ao overhead de expansão
EXPANSION_OVERHEAD_CPI = COMPLETION_CPI - BASE_COMPLETION_CPI

Ciclos paralisados pela Unidade de Ponto-fixo FXU
FXU_STALL_CPI = PM_CMPLU_STALL_FXU/PM_RUN_INST_CMPL

Ciclos paralisados pelas instruções de Ponto-fixo multi-ciclo
FXU_MULTI_CYC_CPI = PM_CMPLU_STALL_DIV/PM_RUN_INST_CMPL

Outros ciclos paralisados pela FXU
FXU_STALL_OTHER_CPI = FXU_STALL_CPI – FXU_MULTI_CYC_CPI

Ciclos com a tabela GCT vazia
GCT_EMPTY_CPI = PM_GCT_NOSLOT_CYC/PM_RUN_INST_CMPL

Ciclos com a tabela GCT vazia devido à falha da cache de instrução
GCT_EMPTY_IC_MISS_CPI = PM_GCT_NOSLOT_IC_MISS/PM_RUN_INST_CMPL

Ciclos com a tabela GCT vazia devido à falha de predição da instrução
GCT_EMPTY_BR_MPRED_CPI = PM_GCT_NOSLOT_BR_MPRED/PM_RUN_INST_CMPL

Ciclos com a tabela GCT vazia devido às falhas de cache e predição de instrução
GCT_EMPTY_BR_MPRED_IC_MISS_CPI = PM_GCT_NOSLOT_BR_MPRED_IC_MISS/PM_RUN_INST_CMPL

Outros ciclos de tabela GCT vazia
GCT_EMPTY_OTHER_CPI = (PM_GCT_NOSLOT_CYC - PM_GCT_NOSLOT_IC_MISS – PM_GCT_NOSLOT_BR_MPRED - PM_GCT_NOSLOT_BR_MPRED_IC_MISS) / PM_RUN_INST_CMPL

Ciclos paralisados pela unidade de busca da instrução IFU
IFU_STALL_CPI = PM_CMPLU_STALL_IFU/PM_RUN_INST_CMPL

Ciclos paralisados pela unidade de execução BRU
IFU_STALL_BRU_CPI = PM_CMPLU_STALL_BRU/PM_RUN_INST_CMPL

Ciclos paralisados por outras operações IFU
IFU_STALL_OTHER_CPI = IFU_STALL_CPI – IFU_STALL_BRU_CPI

Ciclos paralisados pela unidade de Load/Store LSU
LSU_STALL_CPI = PM_CMPLU_STALL_LSU / PM_RUN_INST_CMPL

Ciclos paralisados por rejeições na unidade LSU
LSU_STALL_REJECT_CPI = PM_CMPLU_STALL_REJECT / PM_RUN_INST_CMPL

Ciclos paralisados por traduções ERAT
LSU_STALL_ERAT_MISS_CPI = PM_CMPLU_STALL_ERAT_MISS / PM_RUN_INST_CMPL

Ciclos paralisados por outras rejeições na unidade LSU
LSU_STALL_REJECT_OTHER_CPI = LSU_STALL_REJECT_CPI - LSU_STALL_ERAT_MISS_CPI

Ciclos paralisados devido à falha de perda da cache de dados L1
LSU_STALL_DCACHE_MISS_CPI = PM_CMPLU_STALL_DCACHE_MISS/PM_RUN_INST_CMPL

Ciclos paralisados por outras operações na LSU
LSU_STALL_OTHER_CPI = LSU_STALL_CPI - LSU_STALL_REJECT_CPI - LSU_STALL_DCACHE_MISS_CPI – LSU_STALL_STORE_CPI

Outros ciclos paralisados
OTHER_STALL_CPI = STALL_CPI - FXU_STALL_CPI - VSU_STALL_CPI - LSU_STALL_CPI - IFU_STALL_CPI - SMT_STALL_CPI

Total de ciclos por instrução
RUN_CPI = PM_RUN_CYC / PM_RUN_INST_CMPL

Ciclos paralisados devido ao Multi-Threading simétrico (SMT)
SMT_STALL_CPI = PM_CMPLU_STALL_THRD / PM_RUN_INST_CMPL

Ciclos paralisados
STALL_CPI = PM_CMPLU_STALL / PM_RUN_INST_CMPL

Ciclos paralisados pela unidade vetor e escalar
VSU_STALL_CPI = (PM_CMPLU_STALL_SCALAR + PM_CMPLU_STALL_VECTOR + PM_CMPLU_STALL_DFU) / PM_RUN_INST_CMPL

Ciclos paralisados pela unidade decimal de ponto-flutuante
VSU_STALL_DFU_CPI = PM_CMPLU_STALL_DFU / PM_RUN_INST_CMPL

Ciclos paralisados devido à operações VSU escalar
VSU_STALL_SCALAR_CPI = PM_CMPLU_STALL_SCALAR / PM_RUN_INST_CMPL

Ciclos paralisados devido à operações VSU escalar longo
VSU_STALL_SCALAR_LONG_CPI = PM_CMPLU_STALL_SCALAR_LONG / PM_RUN_INST_CMPL

Ciclos paralisados devido à outras operações VSU escalar
VSU_STALL_SCALAR_OTHER_CPI = VSU_STALL_SCALAR_CPI – VSU_STALL_SCALAR_LONG_CPI

Ciclos paralisados devido à operações VSU de vetor
VSU_STALL_VECTOR_CPI = PM_CMPLU_STALL_VECTOR / PM_RUN_INST_CMPL

Ciclos paralisados devido à operações VSU de vetor longo
VSU_STALL_VECTOR_LONG_CPI = PM_CMPLU_STALL_VECTOR_LONG / PM_RUN_INST_CMPL

Ciclos paralisados devido à outras operações VSU de vetor
VSU_STALL_VECTOR_OTHER_CPI = VSU_STALL_VECTOR_CPI - VSU_STALL_VECTOR_LONG_CPI

A tabela a seguir mostra como essas métricas são representadas na hierarquia de eventos do processador POWER7:

Tabela 3: Representação hierárquica das métricas da análise de CPI

Agora que os eventos e as métricas foram apresentadas podemos focar mais na métrica RUN_CPI. Repare que apenas os ciclos não ociosos são usados para o cálculo de CPI. O total de ciclos por instrução é dado pela divisão dos ciclos não ociosos por instruções completadas, como mostrado a seguir:

RUN_CPI =  PM_RUN_CYC / PM_RUN_INST_CMPL

Ciclos por Instrução é igual ao total de ciclos não ociosos dividido pelo número de instruções completadas.


Coletando informações dos contadores

A técnica de profiling é uma abordagem bastante comum para coletar dados de tempo e utilização de recursos para um workload. Através do profiling é possível identificar problemas nas instruções e nos dados, além de encontrar áreas potenciais para melhoria de performance no código de um programa.

Nesta seção fazemos uma análise de CPI detalhada através de profiling dos eventos de hardware fornecidos pela PMU. Perf e Oprofile são as ferramentas mais populares de performance para profiling e análise de CPI em Linux.

Perf

A tabela a seguir contém os eventos de hardware com seus respectivos códigos pelos quais eles são mapeados pela ferramenta perf.

EventoCódigo
PM_1PLUS_PPC_CMPL0x100f2
PM_CMPLU_STALL0x4000a
PM_CMPLU_STALL_BRU0x4004e
PM_CMPLU_STALL_DCACHE_MISS0x20016
PM_CMPLU_STALL_DFU0x2003c
PM_CMPLU_STALL_DIV0x40014
PM_CMPLU_STALL_ERAT_MISS0x40018
PM_CMPLU_STALL_FXU0x20014
PM_CMPLU_STALL_IFU0x4004c
PM_CMPLU_STALL_LSU0x20012
PM_CMPLU_STALL_REJECT0x40016
PM_CMPLU_STALL_SCALAR0x40012
PM_CMPLU_STALL_SCALAR_LONG0x20018
PM_CMPLU_STALL_STORE0X2004a
PM_CMPLU_STALL_THRD0x1001c
PM_CMPLU_STALL_VECTOR0x2001c
M_CMPLU_STALL_VECTOR_LONG0x4004a
PM_GCT_NOSLOT_BR_MPRED0x4001a
PM_GCT_NOSLOT_BR_MPRED_IC_MISS0x4001c
PM_GCT_NOSLOT_CYC0x100f8
PM_GCT_NOSLOT_IC_MISS0x2001a
PM_GRP_CMPL0x100f2
PM_RUN_CYC0x200f4
PM_RUN_INST_CMPL0x400fa

Tabela 2: Códigos dos eventos de hardware para o perf

A ferramenta perf deve ser executada em um comando ou programa. No entanto, como queremos coletar as estatísticas para a duração de um workload com um conjunto de comandos e programas, não podemos usar o perf em um único programa. Ao invés disso, rodamos o perf em um comando sleep com tempo longo – um dia, usando a opção -a para coletar todas as estatísticas. Quando o workload termina, matamos o processo do comando sleep, o que faz com que o perf retorne as estatísticas e termine. Desta forma os contadores registram os eventos que aconteceram no sistema durante a execução do workload. Esta abordagem pode usada para analisar o que acontece em todo o sistema sem a necessidade do perf saber qual comando ou programa está sendo monitorado.

Importante: O perf abre muitos arquivos para coletar as estatísticas do sistema. Certifique-se de que o limite máximo de arquivos que podem ser abertos comporta a quantidade de eventos monitorados. Consulte os comandos ulimit e lsof para mais informações.

O exemplo a seguir mostra como coletar os contadores referentes à análise de CPI no sistema:

$ perf stat -a --event=r400fa --event=r100f2 --event=r4001a --event=r100f8
--event=r4001c --event=r2001a --event=r200f4 --event=r2004a --event=r4004a
--event=r4004e --event=r4004c --event=r20016 --event=r40018 --event=r20012
--event=r40012 --event=r20018 --event=r4000a --event=r2001c --event=r2003c
--event=r1001c --event=r1003c --event=r20014 --event=r40014 --event=r30004 sleep 1d

 Performance counter stats for 'sleep 1d':

    20642995717523 raw 0x400fa                            [17.68%]
     6351700835673 raw 0x100f2                            [18.91%]
     6592401749617 raw 0x4001a                            [15.03%]
    27815093807308 raw 0x100f8                            [ 8.08%]
      255657047028 raw 0x4001c                            [ 7.99%]
     1717232354921 raw 0x2001a                            [12.04%]
   346189430203434 raw 0x200f4                            [16.04%]
     3960811777916 raw 0x2004a                            [ 7.99%]
      173599336466 raw 0x4004a                            [11.98%]
    80958163849688 raw 0x4004e                            [ 3.98%]
    81233385317385 raw 0x4004c                            [ 3.99%]
   191318194194427 raw 0x20016                            [ 7.98%]
      564560376035 raw 0x40018                            [ 7.98%]
   216813544374568 raw 0x20012                            [ 7.98%]
     8553663838719 raw 0x40016                            [ 7.98%]
       16396730779 raw 0x40012                            [ 4.12%]
      243815645456 raw 0x20018                            [ 8.37%]
   310390576740069 raw 0x4000a                            [ 8.39%]
      250666680056 raw 0x2001c                            [ 8.15%]
      296680513415 raw 0x2003c                            [ 4.14%]
      109059471221 raw 0x1001c                            [ 8.35%]
     2515554688530 raw 0x1003c                            [ 4.08%]
    13535182790745 raw 0x20014                            [ 8.09%]
      223953581841 raw 0x40014                            [12.10%]
     6345222575236 raw 0x30004                            [16.11%]

    1677.714054029 seconds time elapsed

Exemplo 1: Análise de Ciclos por Instrução (CPI) usando perf

Os percentuais da terceira coluna representam a parcela de tempo em que os contadores estão efetivamente registrando os eventos em relação ao tempo em que estão ativos. Se reduzirmos o número de eventos para 2 ou 3 eventos o kernel não terá que rotacionar os eventos através dos contadores disponíveis, dando aos eventos 100% de uso dos contadores.

O exemplo acima aponta para um número de falhas de cache (cache miss) elevado, o que implica muitas vezes em queda de performance da aplicação e do sistema como um todo. Existem diversas técnicas para reduzir falhas de cache como reescrever linhas do código que acessam os dados da memória de uma forma eficiente. O programa a seguir mostra como a simples troca de ordem de loops encadeados pode fazer uma grande diferença na otimização do código.

#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 


void main(){ 

int a[1000][1000], i, j; 

srand ( time(NULL) ); 

 for (i=0;i<1000;i++){ 
    for (j=0;j<1000;j++){ 
      a[j][i] = rand() % 100; 
    } 
  } 

  for (i=0;i<1000;i++){ 
    for (j=0;j<1000;j++){ 
      a[j][i] = a[j][i]*2; 
    } 
  } 

return; 

}

Note que a cada iteração do loop o acesso à memória resulta em falha da cache por causa dos longos saltos dados pelo índice j no loop interno. Esses saltos são ilustrados no exemplo a seguir:

Nessa analogia a memória é acessada horizontalmente e quando j é incrementado, todos os índices i são saltados. Por outro lado, se invertermos os loops, o salto do índice j será de 1 e os dados serão acessados sequencialemente. O código invertido e o respectivo acesso à memória são mostrados a seguir:

     for (j=0;j<1000;j++){ 
    for (i=0;i<1000;i++){

Para exemplificar na prática as duas situações descritas acima usamos a ferramenta de profiling oprofile para contar os eventos de cache miss enquanto o programa é executado. O programa original é o cache_miss e o modificado com o loop invertido nomeado de cache_hit.

opcontrol --deinit

opcontrol -e PM_DATA_FROM_L2MISS_GRP16:1000

opcontrol --init

opcontrol --reset; opcontrol --start; ./cache_hit ; 
  opcontrol --stop ; opreport > L2_hit.out 
Signalling daemon... done 
Profiler running. 
Stopping profiling.

opcontrol --reset; opcontrol --start; ./cache_miss ;
  opcontrol --stop ; opreport > L2_miss.out 
Signalling daemon... done 
Profiler running. 
Stopping profiling.

cat L2* |grep cache_ 
        1  0.0766 cache_hit 
       32  2.3616 cache_miss

Através desse simples exemplo é fácil identificar a redução de eventos de cache miss no programa modificado cache_hit.


Conclusão

A análise de CPI permite determinar o real uso do hardware para um determinado workload. Essa abordagem vai além de simplesmente medir a performance de aplicações, compiladores, ou do próprio hardware. A métrica “Ciclos Por Instrução” aponta os gargalos na execução das instruções e facilita no desenvolvimento e ajuste de aplicações otimizadas que utilizam o hardware de maneira eficiente.


Referências

https://www.power.org/events/Power7/

http://oprofile.sourceforge.net/docs/ppc64-power7-events.php

http://www.ibm.com/developerworks/power/library/pa-cpipower1/

https://www.ibm.com/developerworks/wikis/display/LinuxP/Taking+advantage+of+oprofile

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
ArticleID=852005
ArticleTitle=Ciclos Por Instrução: Análise de Performance em Power7
publish-date=12172012