Execução concorrente de instruções: Pipelines
Execução concorrente de instruções: Pipelines
Execução concorrente de instruções: Pipelines
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
<strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong><br />
João Canas Ferreira<br />
Outubro <strong>de</strong> 2004<br />
Contém figuras <strong>de</strong> “Computer Architecture: A Quantitative Approach”, J. Hennessey & D. Patterson, 3 a . ed., MKP<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 1/33<br />
Assuntos<br />
1 <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong><br />
Conceito e finalida<strong>de</strong><br />
Dependências entre <strong>instruções</strong><br />
2 Fundamentos da operação <strong>de</strong> pipelines<br />
O que são pipelines<br />
Pipeline <strong>de</strong> 5 andares<br />
3 Resolução <strong>de</strong> conflitos<br />
Conflitos estruturais<br />
Conflitos <strong>de</strong> dados<br />
Conflitos <strong>de</strong> controlo<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 2/33
<strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong> Conceito e finalida<strong>de</strong><br />
O mo<strong>de</strong>lo sequencial <strong>de</strong> computação<br />
No mo<strong>de</strong>lo sequencial <strong>de</strong> computação, a execução <strong>de</strong> um programa<br />
processa-se do seguinte modo:<br />
1. O CPU obtém uma instrução <strong>de</strong> memória.<br />
2. A operação é efectuada.<br />
3. É <strong>de</strong>terminado o en<strong>de</strong>reço da próxima instrução. Repetir a partir <strong>de</strong> 1.<br />
Os efeitos observáveis da execução incluem a alteração <strong>de</strong> registos ou<br />
posições <strong>de</strong> memória, a alteração <strong>de</strong> flags, geração <strong>de</strong> excepções, etc.<br />
Numa implementação que corresponda directamente ao mo<strong>de</strong>lo apenas se<br />
po<strong>de</strong> aumentar o <strong>de</strong>sempenho tornando a execução da operação mais<br />
rápida.<br />
Para ultrapassar esse limite é preciso executar <strong>instruções</strong> parcial ou<br />
totalmente em simultâneo.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 3/33<br />
3.1. O mo<strong>de</strong>lo sequencial permite atribuir <strong>de</strong> uma forma simples um “significado<br />
operacional” a um programa. Tem a gran<strong>de</strong> vantagem e ser relativamente<br />
próximo <strong>de</strong> uma possível implementação em hardware (com a unida<strong>de</strong> <strong>de</strong><br />
controlo implementada por uma máquina <strong>de</strong> estados) e do mo<strong>de</strong>lo formal <strong>de</strong><br />
máquina <strong>de</strong> Turing. Tem a <strong>de</strong>svantagem <strong>de</strong> impor uma or<strong>de</strong>m na execução<br />
<strong>de</strong> <strong>instruções</strong> cuja necessida<strong>de</strong> não <strong>de</strong>corre logicamente do algoritmo a<br />
implementar.<br />
3.2. No passo 3, a <strong>de</strong>terminação da próxima instrução a executar é feita<br />
geralmente <strong>de</strong> forma implícita: trata-se da instrução que resi<strong>de</strong> na posição a<br />
seguir à da instrução executada antes. Apenas as <strong>instruções</strong> <strong>de</strong> controlo <strong>de</strong><br />
fluxo indicam explicitamente a origem da próxima instrução (o “alvo” do<br />
salto).<br />
3.3. São concebíveis conjuntos <strong>de</strong> <strong>instruções</strong> em que cada instrução especifique o<br />
en<strong>de</strong>reço da seguinte (i.e., cada instrução é uma instrução <strong>de</strong> controlo <strong>de</strong><br />
fluxo). Muitos esquemas <strong>de</strong> microcódigo usam uma arquitectura <strong>de</strong>sse tipo.<br />
3
<strong>Execução</strong> <strong>concorrente</strong><br />
<strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong> Conceito e finalida<strong>de</strong><br />
Concorrência parcial: A execução <strong>de</strong> uma instrução é dividida em fases.<br />
Num mesmo instante, diferentes <strong>instruções</strong> po<strong>de</strong>m estar em diferentes<br />
fases da sua execução.<br />
Problema: Dado um programa, i<strong>de</strong>ntificar que <strong>instruções</strong> po<strong>de</strong>m ser<br />
executadas <strong>concorrente</strong>mente, sem alterar os efeitos do programa.<br />
Efeitos do programa: Sequência <strong>de</strong> alterações visíveis do estado da<br />
memória e do processador.<br />
Existem muitas técnicas para <strong>de</strong>tectar e aproveitar o paralelismo entre<br />
<strong>instruções</strong> (i.e. a capacida<strong>de</strong> <strong>de</strong> executar <strong>instruções</strong> <strong>concorrente</strong>mente).<br />
Essa técnicas caem em duas categorias gerais:<br />
1. técnicas dinâmicas — o processador <strong>de</strong>tecta o paralelismo durante a<br />
execução do programa (Pentium III e 4, PowerPC G4);<br />
2. técnicas estáticas — o paralelismo é <strong>de</strong>tectado antes da execução<br />
pelo compilador (IA-64, sistemas embutidos).<br />
Não se trata <strong>de</strong> uma divisão estanque; algumas técnicas po<strong>de</strong>m ser<br />
adoptadas nos dois domínios.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 4/33<br />
4.1. As técnicas estáticas obrigam à recompilação dos programas para cada<br />
mo<strong>de</strong>lo diferente do compilador, mesmo que o conjunto <strong>de</strong> instrução não<br />
mu<strong>de</strong>. Quando a compatibilida<strong>de</strong> binária é importante, apenas as técnicas<br />
dinâmicas po<strong>de</strong> ser usadas. Excepção: Po<strong>de</strong> ser possível produzir um<br />
“tradutor binário” que converte um programa em binário <strong>de</strong> um mo<strong>de</strong>lo para<br />
o outro, refazendo as optimizações necessárias. Consultar, por exemplo,<br />
http://www.research.compaq.com/wrl/projects/om/om.html ou http:<br />
//www.experimentalstuff.com/Technologies/Walkabout/in<strong>de</strong>x.html.<br />
4.2. A utilização <strong>de</strong> técnicas dinâmicas não exclui o emprego das outras.<br />
Consi<strong>de</strong>re-se o caso dos processadores da Intel que implementam a<br />
arquitectura IA-32. A compatibilida<strong>de</strong> binária entre mo<strong>de</strong>los é garantida,<br />
mas a recompilação <strong>de</strong> um programa po<strong>de</strong> permitir a geração <strong>de</strong> código mais<br />
eficiente para um mo<strong>de</strong>lo particular (mas igualmente correcto para qualquer<br />
mo<strong>de</strong>lo).<br />
4
Aspectos básicos<br />
<strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong> Conceito e finalida<strong>de</strong><br />
➛ O quantida<strong>de</strong> <strong>de</strong> paralelismo existente num bloco básico é reduzida:<br />
Blocos básicos têm tipicamente entre 5 e 7 <strong>instruções</strong> em média (MIPS).<br />
➛ Paralelismo simples <strong>de</strong> i<strong>de</strong>ntificar: loop-level parallelism<br />
Paralelismo entre iterações <strong>de</strong> um ciclo.<br />
for (i=1; i
<strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong> Dependências entre <strong>instruções</strong><br />
O conceito <strong>de</strong> <strong>de</strong>pendência entre <strong>instruções</strong><br />
Duas <strong>instruções</strong> in<strong>de</strong>pen<strong>de</strong>ntes po<strong>de</strong>m ser executadas em simultâneo<br />
sem conflitos, <strong>de</strong>s<strong>de</strong> que existam recursos suficientes.<br />
Quando existe uma relação <strong>de</strong> <strong>de</strong>pendência entre duas <strong>instruções</strong>,<br />
estes <strong>de</strong>vem ser executadas em série (embora talvez com sobreposição<br />
parcial).<br />
<strong>de</strong>pendências <strong>de</strong> dados:<br />
<strong>de</strong>pendências verda<strong>de</strong>iras e <strong>de</strong>pendências <strong>de</strong> nomes.<br />
<strong>de</strong>pendências <strong>de</strong> controlo<br />
Um conflito ocorre sempre que existe uma <strong>de</strong>pendência entre<br />
<strong>instruções</strong> suficientemente próximas para que a sobreposição ou<br />
re-or<strong>de</strong>nação modifique a or<strong>de</strong>m <strong>de</strong> acesso ao operando envolvido na<br />
<strong>de</strong>pendência.<br />
A satisfação das <strong>de</strong>pendências garante a manutenção da or<strong>de</strong>m do<br />
programa , i.e., garante que as alterações <strong>de</strong> estado (observáveis) são as<br />
especificadas pelo mo<strong>de</strong>lo sequencial <strong>de</strong> execução.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 6/33<br />
6.1. O conceito <strong>de</strong> <strong>de</strong>pendência <strong>de</strong>pen<strong>de</strong> expressar formalmente a relação <strong>de</strong><br />
precedência entre duas <strong>instruções</strong>.<br />
6.2. Instruções <strong>de</strong>pen<strong>de</strong>ntes não po<strong>de</strong>m executar <strong>de</strong> forma totalmente <strong>concorrente</strong><br />
com as <strong>instruções</strong> <strong>de</strong> que <strong>de</strong>pen<strong>de</strong>m. Isto não implica que uma execução<br />
parcialmente <strong>concorrente</strong> seja impossível, como no caso <strong>de</strong> pipelines.<br />
6.3. Um conflito ocorre sempre que a execução (parcialmente) <strong>concorrente</strong> <strong>de</strong><br />
<strong>instruções</strong> leva à violação <strong>de</strong> uma <strong>de</strong>pendência. Se um conflito ocorre ou não<br />
(para uma dada <strong>de</strong>pendência) <strong>de</strong>pen<strong>de</strong> da organização interna do CPU. De<br />
um modo geral, preten<strong>de</strong>-se minimizar a ocorrência <strong>de</strong> conflitos.<br />
6.4. Um conflito po<strong>de</strong> ser sempre evitado se a execução <strong>de</strong> <strong>instruções</strong> retomar o<br />
modo <strong>de</strong> funcionamento completamente sequencial. Ou seja, quando ocorre<br />
um conflito é sempre possível resolvê-lo atrasando a execução das <strong>instruções</strong><br />
<strong>de</strong>pen<strong>de</strong>ntes. Assim garante-se para cada instrução que aquelas <strong>de</strong> que<br />
<strong>de</strong>pen<strong>de</strong> já foram certamente executadas.<br />
6
Dependências <strong>de</strong> dados<br />
<strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong> Dependências entre <strong>instruções</strong><br />
A instrução j <strong>de</strong>pen<strong>de</strong> da instrução i (i is data-<strong>de</strong>pen<strong>de</strong>nt on j) se ocorrer<br />
uma das duas situações seguintes:<br />
1. a instrução i produz um resultado que po<strong>de</strong> ser usado pela instrução j;<br />
2. a instrução j <strong>de</strong>pen<strong>de</strong> da instrução k, que, por sua vez, <strong>de</strong>pen<strong>de</strong> da<br />
instrução i.<br />
A ca<strong>de</strong>ia <strong>de</strong> <strong>de</strong>pendências po<strong>de</strong> englobar o programa completo.<br />
Exemplo com 3 <strong>de</strong>pendências:<br />
Loop: L.D F0 , 0(R1) ; F0
<strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong> Dependências entre <strong>instruções</strong><br />
Características das <strong>de</strong>pendências <strong>de</strong> dados<br />
A presença <strong>de</strong> uma <strong>de</strong>pendência <strong>de</strong> dados numa sequência <strong>de</strong><br />
<strong>instruções</strong> reflecte uma <strong>de</strong>pendência no programa original:<br />
<strong>de</strong>pendências são proprieda<strong>de</strong>s dos programas.<br />
O facto <strong>de</strong> uma <strong>de</strong>pendência resultar na <strong>de</strong>tecção <strong>de</strong> um conflito e/ou<br />
<strong>de</strong> causar um protelamento são proprieda<strong>de</strong>s da organização da<br />
pipeline.<br />
Uma <strong>de</strong>pendência:<br />
1. indica a possibilida<strong>de</strong> <strong>de</strong> existência <strong>de</strong> um conflito;<br />
2. <strong>de</strong>termina a or<strong>de</strong>m <strong>de</strong> cálculo;<br />
3. estabelece um limite superior para a quantida<strong>de</strong> <strong>de</strong> paralelismo que<br />
po<strong>de</strong> ser aproveitado.<br />
Fluxo <strong>de</strong> dados: (i) por registos [e flags]; (ii) por memória.<br />
Soluções:<br />
1. manter a <strong>de</strong>pendência, mas evitar o conflito;<br />
2. eliminar a <strong>de</strong>pendência por transformação do código.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 8/33<br />
8.1. Dependências causadas por transferências <strong>de</strong> informação via memória po<strong>de</strong>m<br />
ser difíceis <strong>de</strong> <strong>de</strong>tectar. Por exemplo, as <strong>instruções</strong> L.D R1, 100(R4) e<br />
L.D R1, 64(R6) po<strong>de</strong>m <strong>de</strong>signar a mesma posição <strong>de</strong> memória (por<br />
exemplo, se R4=0 e R6=36). Neste caso, diz-se que os en<strong>de</strong>reços efectivos<br />
são iguais.<br />
8.2. O en<strong>de</strong>reço efectivo <strong>de</strong> uma instrução também po<strong>de</strong> variar durante a<br />
execução do programa: basta que o conteúdo do registo associado mu<strong>de</strong>.<br />
Portanto, para efeitos <strong>de</strong> <strong>de</strong>tecção <strong>de</strong> conflitos, não basta analisar os códigos<br />
das <strong>instruções</strong>, é necessário ter em conta também o estado dos registos.<br />
8.3. Quando não é possível garantir a ausência <strong>de</strong> conflitos, é necessário assumir<br />
que eles po<strong>de</strong>m existir, caso contrário, não seria possível garantir a execução<br />
correcta do programa.<br />
8
Dependências <strong>de</strong> nome<br />
<strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong> Dependências entre <strong>instruções</strong><br />
Uma <strong>de</strong>pendência <strong>de</strong> nome ocorre quando duas <strong>instruções</strong> usam o mesmo<br />
registo ou posição <strong>de</strong> memória (um «nome»), mas sem que haja um fluxo<br />
<strong>de</strong> dados associado com esse nome. Existem dois tipos <strong>de</strong> <strong>de</strong>pendências <strong>de</strong><br />
nome envolvendo uma instrução i que prece<strong>de</strong> a instrução j num programa:<br />
Uma anti<strong>de</strong>pendência entre i e j ocorre quando a instrução j escreve<br />
num registo ou posição <strong>de</strong> memória lido pela instrução i.<br />
Uma <strong>de</strong>pendência <strong>de</strong> saída ocorre quando ambas as <strong>instruções</strong><br />
escrevem no mesmo registo ou posição <strong>de</strong> memória. A or<strong>de</strong>m das<br />
<strong>instruções</strong> <strong>de</strong>ve ser preservada para garantir que o valor final<br />
correspon<strong>de</strong> a j.<br />
Como não se trata <strong>de</strong> verda<strong>de</strong>iras <strong>de</strong>pendências, as <strong>instruções</strong> i e j po<strong>de</strong>m<br />
ser executadas simultaneamente (ou por or<strong>de</strong>m diferente) <strong>de</strong>s<strong>de</strong> que o<br />
«nome» usado seja mudado. Esta operação é mais fácil <strong>de</strong> executar (pelo<br />
compilador ou processador) para registos: register renaming.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 9/33<br />
9.1. Dependências <strong>de</strong> nome ocorrem, por exemplo, quando um registo (associado<br />
a uma variável) é reutilizado para guardar o valor <strong>de</strong> outra variável.<br />
9.2. Em geral, <strong>de</strong>pendências <strong>de</strong> nome envolvendo registos po<strong>de</strong>m ser evitadas se<br />
existirem registos temporários que permitam guardar todos os valores. Todas<br />
as <strong>instruções</strong> que referem o «nome» original <strong>de</strong>vem ser modificadas<br />
dinamicamente para usarem os dados dos registos temporários apropriados.<br />
9
Conflitos <strong>de</strong> dados<br />
<strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong> Dependências entre <strong>instruções</strong><br />
Conflitos <strong>de</strong> dados possíveis entre duas <strong>instruções</strong> i e j (que ocorrem no<br />
programa por esta or<strong>de</strong>m):<br />
RAW (read after write) — j tenta ler uma fonte antes que i aí coloque<br />
um valor. Correspon<strong>de</strong> a uma verda<strong>de</strong>ira <strong>de</strong>pendência <strong>de</strong> dados.<br />
WAW (write after write) — j tenta escrever num local antes <strong>de</strong> i.<br />
Correspon<strong>de</strong> a uma <strong>de</strong>pendência <strong>de</strong> saída. Ocorre em pipelines que<br />
efectuam escritas em mais que um andar ou que permitem que<br />
<strong>instruções</strong> continuem mesmo quando uma instrução anterior é<br />
protelada.<br />
WAR (write after read) — j tenta escrever num local antes <strong>de</strong> este<br />
ser lido por i. Correspon<strong>de</strong> a uma anti<strong>de</strong>pendência. Não ocorre na<br />
maioria das pipelines com emissão estática <strong>de</strong> <strong>instruções</strong>.<br />
RAR (read after read) não constitui um conflito.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 10/33<br />
10.1. O nome dos conflitos é dado <strong>de</strong> acordo com a or<strong>de</strong>m do programa que <strong>de</strong>ve<br />
ser preservada.<br />
10.2. Conflitos do tipo war ten<strong>de</strong>m a surgir apenas em processadores que emitem<br />
<strong>instruções</strong> fora <strong>de</strong> or<strong>de</strong>m (emitir uma instrução = iniciar a sua execução) ou<br />
que suportam conjuntos heterogéneos <strong>de</strong> <strong>instruções</strong> em que algumas<br />
<strong>instruções</strong> alteram dados no início da execução e outras lêem dados nas fases<br />
finais da execução. Muitos processadores nunca po<strong>de</strong>m ter conflitos <strong>de</strong>ste<br />
tipo.<br />
10.3. Conflitos do tipo waw também não correm nas organizações <strong>concorrente</strong>s<br />
mais simples. Por exemplo, a pipeline básica analisada mais à frente não tem<br />
<strong>de</strong>stes conflitos.<br />
10
<strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong> Dependências entre <strong>instruções</strong><br />
Dependências <strong>de</strong> controlo<br />
Uma <strong>de</strong>pendência <strong>de</strong> controlo <strong>de</strong>termina a or<strong>de</strong>m <strong>de</strong> uma instrução i face<br />
a uma instrução <strong>de</strong> salto condicional. Todas as <strong>instruções</strong> <strong>de</strong> um<br />
programa, excepto as do primeiro bloco básico, <strong>de</strong>pen<strong>de</strong>m <strong>de</strong> algum<br />
conjunto <strong>de</strong> saltos condicionais.<br />
Dependências <strong>de</strong> controlo impõem duas restrições:<br />
Uma instrução <strong>de</strong>pen<strong>de</strong>nte <strong>de</strong> um salto não po<strong>de</strong> ser colocada antes do<br />
salto, porque já não seria controlada pelo instrução <strong>de</strong> salto condicional.<br />
Uma instrução que não está <strong>de</strong>pen<strong>de</strong>nte <strong>de</strong> um salto não po<strong>de</strong> ser movida<br />
para <strong>de</strong>pois <strong>de</strong> um salto, porque passaria a ser controlada por esse salto.<br />
No caso mais simples, <strong>de</strong>pendências <strong>de</strong> controlo são preservadas porque<br />
1. as <strong>instruções</strong> são executadas pela or<strong>de</strong>m em que surgem no programa;<br />
2. o controlo <strong>de</strong> conflitos garante que uma instrução não é executada antes <strong>de</strong> se<br />
conhecer o <strong>de</strong>stino do salto.<br />
Respeitar as <strong>de</strong>pendências <strong>de</strong> controlo não é um fim em si; o que se quer<br />
garantir é o fluxo <strong>de</strong> dados e o comportamento face a excepções.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 11/33<br />
11.1. Exemplo <strong>de</strong> uma <strong>de</strong>pendência <strong>de</strong> controlo simples:<br />
if cond1 instr1;<br />
if cond2 instr2;<br />
A instrução instr1 apresenta uma <strong>de</strong>pendência <strong>de</strong> controlo em relação a<br />
cond1 e instr2 tem uma <strong>de</strong>pendência <strong>de</strong> controlo <strong>de</strong> cond2, mas não <strong>de</strong><br />
cond1.<br />
11
<strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong> Dependências entre <strong>instruções</strong><br />
Comportamento correcto na presença <strong>de</strong> excepções<br />
➛ Comportamento correcto na presença <strong>de</strong> excepções significa que<br />
qualquer alteração da or<strong>de</strong>m <strong>de</strong> execução das <strong>instruções</strong> não modifica a<br />
forma como as excepção são levantadas no programa.<br />
➛ Frequentemente usa-se uma condição mais relaxada: a nova or<strong>de</strong>m não<br />
<strong>de</strong>ve causar a ocorrência <strong>de</strong> novas excepções no programa.<br />
DADDU R2 , R3, R4<br />
BEQZ R2 , L1 ; saltar para L1 se R2=0<br />
LW R1, 0( R2 )<br />
L1: . . .<br />
Existe uma <strong>de</strong>pendência <strong>de</strong> dados envolvendo R2.<br />
É possível trocar as duas últimas <strong>instruções</strong>? Não existe nenhuma <strong>de</strong><br />
<strong>de</strong>pendência <strong>de</strong> dados entre elas. Mas a instrução LW po<strong>de</strong> causar uma<br />
excepção no acesso a memória.<br />
Uma técnica <strong>de</strong>signada por especulação permite ultrapassar este problema.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 12/33<br />
12.1. A <strong>de</strong>pendência <strong>de</strong> dados envolvendo R2 impõe que a primeira instrução seja<br />
executada em primeiro lugar, mas não impõe uma or<strong>de</strong>m relativa entre as<br />
outras duas; essa or<strong>de</strong>m é imposta pela <strong>de</strong>pendência <strong>de</strong> controlo <strong>de</strong> LW em<br />
relação a BEQZ. Não existe uma <strong>de</strong>pendência <strong>de</strong> dados entre estas duas<br />
<strong>instruções</strong>.<br />
12.2. Como é que a instrução LW po<strong>de</strong> causar uma excepção? Por exemplo,<br />
ace<strong>de</strong>ndo a uma zona <strong>de</strong> memória que não pertença ao seu processo. Assim,<br />
po<strong>de</strong>ria ocorrer uma excepção que não surgiria se a or<strong>de</strong>m do programa fosse<br />
alterada.<br />
12.3. As técnicas <strong>de</strong> especulação <strong>de</strong>vem ser capazes <strong>de</strong> anular o lançamento da<br />
excepção caso o salto condicional seja efectivamente tomado.<br />
12
<strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong> Dependências entre <strong>instruções</strong><br />
Preservação do fluxo <strong>de</strong> dados<br />
Fluxo <strong>de</strong> dados: fluxo <strong>de</strong> valores entre as <strong>instruções</strong> que os produzem e as<br />
que os consomem. As <strong>instruções</strong> <strong>de</strong> salto condicional fazem com que o<br />
fluxo seja dinâmico, i.e., o valor <strong>de</strong> um dado registo/posição <strong>de</strong> memória<br />
po<strong>de</strong> provir <strong>de</strong> diferentes origens.<br />
DADDU R1, R2, R3<br />
BEQZ R4, L<br />
DSUBU R1, R5, R6<br />
L: . . .<br />
OR R7, R1, R8<br />
DSUBU não po<strong>de</strong> ser colocada antes do salto por alterar o fluxo <strong>de</strong> dados<br />
<strong>de</strong> OR. O valor <strong>de</strong> R1 <strong>de</strong>pen<strong>de</strong> do salto BEQZ.<br />
Por vezes, é possível <strong>de</strong>terminar que o <strong>de</strong>srespeito <strong>de</strong> uma <strong>de</strong>pendência <strong>de</strong><br />
controlo não afecta o fluxo ou a geração <strong>de</strong> excepções. Esta tarefa é<br />
geralmente efectuada pelos compiladores.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 13/33<br />
13.1. Exemplo <strong>de</strong> uma situação DADDU em R1, que R2, umaR3<strong>de</strong>pendência <strong>de</strong> controlo po<strong>de</strong> ser<br />
BEQZ R12, skipnext<br />
<strong>de</strong>srespeitada:<br />
DSUBU R4, R5, R6 ; po<strong>de</strong> ser "adiantada"<br />
DADDU R5, R4, R9<br />
skipnext: OR R7, R8, R9 ; R4 não é usado mais<br />
Suponhamos que o registo R4não era usado no bloco <strong>de</strong> código colocado após<br />
a etiqueta skipnext. Então passar a instrução DSUBU para antes do salto<br />
não alteraria os efeitos do programa (o item <strong>de</strong> dados em R4 está “morto”).<br />
13.2. Determinar se os dados guardados num registo estão “vivos” (vão ser ainda<br />
usados) ou “mortos” (não são mais usados) é uma das tarefas que<br />
geralmente incumbe ao compilador (para saber quando po<strong>de</strong> reutilizar<br />
registos).<br />
13
<strong>Pipelines</strong><br />
Fundamentos da operação <strong>de</strong> pipelines O que são pipelines<br />
Pipelining é uma técnica <strong>de</strong> implementação em que a execução <strong>de</strong><br />
múltiplas <strong>instruções</strong> é feita <strong>concorrente</strong>mente.<br />
Analogia: Linha <strong>de</strong> montagem.<br />
A B C D<br />
andar 1 andar 2 andar 3 andar 4<br />
ciclo 1 2 3 4 5 6 · · ·<br />
inst 1 A B C D<br />
inst 2 A B C D<br />
inst 3 A B C D<br />
inst 4 A B C · · ·<br />
inst 5 A B · · ·<br />
inst 6 A · · ·<br />
Notas:<br />
Cada andar trabalha <strong>concorrente</strong>mente<br />
com os outros, embora sobre<br />
uma “parte” diferente da instrução.<br />
débito: n o <strong>de</strong> <strong>instruções</strong> que são completadas<br />
(i.e. abandonam a pipeline) por unida<strong>de</strong><br />
<strong>de</strong> tempo.<br />
ciclo do processador: tempo necessário<br />
para “mover” a instrução através <strong>de</strong> 1 andar.<br />
O ciclo do processador é <strong>de</strong>terminado pelo<br />
andar mais lento.<br />
I<strong>de</strong>al: Equilibrar o tempo <strong>de</strong> todos os andares.<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 14/33<br />
14.1. Em cada instante po<strong>de</strong>m estar até 4 <strong>instruções</strong> em execução (neste<br />
exemplo); em cada ciclo (a partir do 4) é terminada uma instrução.<br />
14.2. Como os andares estão interligados, é necessário que todos estejam prontos<br />
ao mesmo tempo: esse tempo constitui o período do ciclo <strong>de</strong> relógio do<br />
processador.<br />
14.3. Porque os andares <strong>de</strong>vem prosseguir todos ao mesmo tempo, a duração do<br />
ciclo é <strong>de</strong>terminado pelo tempo necessário para que o andar mais lento<br />
execute a sua tarefa.<br />
14.4. Se o tempo <strong>de</strong> processamento <strong>de</strong> todos os andares for idêntico, então o<br />
tempo médio por operação é (em condições i<strong>de</strong>ais)<br />
tempo por instrução do CPU sem pipeline<br />
número <strong>de</strong> andares<br />
14.5. Pipelining é tipicamente invisível para o programador (em CPUS com<br />
interlocks).<br />
14
Fundamentos da operação <strong>de</strong> pipelines Pipeline <strong>de</strong> 5 andares<br />
Um conjunto <strong>de</strong> <strong>instruções</strong> RISC básico<br />
Para efeitos <strong>de</strong> análise das questões que surgem em pipelines, usaremos<br />
um subconjunto do MIPS64 (ver tb. a página www da disciplina).<br />
➛ Todas as operações sobre dados aplicam-se a registos <strong>de</strong> 64 bits e<br />
modificam todo o registo;<br />
➛ Apenas <strong>instruções</strong> load/store afectam a memória; po<strong>de</strong>m operar sobre<br />
parte <strong>de</strong> um registo (por exemplo, afectando 32 bits <strong>de</strong> 64);<br />
➛ As <strong>instruções</strong> têm todas o mesmo comprimento e os formatos<br />
diferentes são poucos.<br />
➛ Instruções da ALU: Tomam dois registos, ou um registo e um valor<br />
imediato <strong>de</strong> 16 bits, como operandos e guardam o resultado num<br />
terceiro registo.<br />
➛ Instruções load/store: Tomam um registo (a base) e um<br />
<strong>de</strong>slocamento (<strong>de</strong> 16 bits) como operandos. Um segundo registo é o<br />
<strong>de</strong>stino/origem da transferência.<br />
➛ Saltos condicionais e incondicionais: Os saltos condicionais<br />
baseiam-se no resultado da comparação entre dois registos.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 15/33<br />
15.1. As <strong>instruções</strong> indicadas constituem um subconjunto <strong>de</strong> MIPS64. Esta<br />
arquitectura <strong>de</strong> conjunto <strong>de</strong> <strong>instruções</strong> será analisada com mais pormenor<br />
numa aula teórico-prática.<br />
15.2. Os saltos condicionais são relativos e têm um <strong>de</strong>slocamento <strong>de</strong> 16 bits (são<br />
interpretados como números com sinal).<br />
15.3. Todas as <strong>instruções</strong> têm todas o mesmo tamanho: 32 bits.<br />
15
Fundamentos da operação <strong>de</strong> pipelines Pipeline <strong>de</strong> 5 andares<br />
Implementação multi-ciclo sem pipelining<br />
1. IF (instruction fetch): Usa o en<strong>de</strong>reço contido no contador <strong>de</strong><br />
programa (PC) para ir buscar a instrução actual a memória. Actualiza<br />
PC: PC ← PC+4.<br />
2. ID (instruction <strong>de</strong>co<strong>de</strong>/register fetch): Descodifica a instrução e lê<br />
os registos fonte. Compara registos. Efectua extensão do<br />
<strong>de</strong>slocamento. Calcula en<strong>de</strong>reço <strong>de</strong> <strong>de</strong>stino e actualiza PC (se saltar).<br />
3. EX (execution/effective address): Uma <strong>de</strong> 3 operações: (i) calcula<br />
en<strong>de</strong>reço efectivo para acesso a memória; (ii) a ALU executa a<br />
operação sobre os registos; (iii) a ALU executa operação sobre registo e<br />
valor imediato.<br />
4. MEM (memory access): Para <strong>instruções</strong> load/store: transferir dados.<br />
5. WB (write-back): Instrução <strong>de</strong> ALU ou load: guardar dados em<br />
registo.<br />
Saltos: 2 ciclos, store: 4 ciclos, outras: 5 ciclos. Globalmente, CPI=4.54 se<br />
assumirmos frequências <strong>de</strong> 12%, 10%, e 78%, respectivamente.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 16/33<br />
16.1. Este esquema apresenta uma divisão possível das etapas pelas quais passa a<br />
execução <strong>de</strong> cada instrução. Trata-se <strong>de</strong> um passo preliminar, com vista a<br />
simplificar a <strong>de</strong>scrição do funcionamento da pipeline básica.<br />
16.2. Nem todas as <strong>instruções</strong> necessitam <strong>de</strong> todas as etapas. As <strong>instruções</strong> <strong>de</strong><br />
salto terminam na 2 a etapa. As <strong>instruções</strong> <strong>de</strong> escrita em memória terminam<br />
na 4 a etapa.<br />
16.3. As <strong>instruções</strong> <strong>de</strong> ALU não fazem nada na etapa 4, terminando na etapa 5.<br />
Seria possível antecipar a última tarefa da etapa 5 para a etapa 5. Contudo,<br />
isso já não será possível na implementação em pipeline, pelo que também<br />
não é feito aqui.<br />
16
Fundamentos da operação <strong>de</strong> pipelines Pipeline <strong>de</strong> 5 andares<br />
Pipeline <strong>de</strong> cinco andares<br />
O processamento po<strong>de</strong> ser implementado com pipeline <strong>de</strong> 5 andares (aliás<br />
a divisão <strong>de</strong> tarefas da transparência anterior já foi feita a pensar nisso).<br />
Ciclo <strong>de</strong> processador<br />
Instrução 1 2 3 4 5 6 7 8 9<br />
i IF ID EX MEM WB<br />
i + 1 IF ID EX MEM WB<br />
i + 2 IF ID EX MEM WB<br />
i + 3 IF ID EX MEM WB<br />
i + 4 IF ID EX MEM WB<br />
Restrição: Não é possível usar os mesmos recursos em duas tarefas<br />
diferentes no mesmo ciclo! Memórias cache separadas para dados e<br />
<strong>instruções</strong> evitam conflitos entre IF e MEM.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 17/33<br />
17.1. A fase IF tem um acesso implícito a memória, pelo que po<strong>de</strong>ria interferir<br />
com <strong>instruções</strong> na fase MEM. Para evitar um conflito, vamos assumir que<br />
memória <strong>de</strong> <strong>instruções</strong> e memória <strong>de</strong> dados são diferentes. Embora tal não<br />
corresponda estritamente à realida<strong>de</strong>, a utilização <strong>de</strong> caches separadas para<br />
dados e <strong>instruções</strong> faz com que seja uma aproximação razoável.<br />
17.2. Para já assumimos que o acesso a memória po<strong>de</strong> ser feito apenas num ciclo<br />
<strong>de</strong> relógio do processador. Trata-se <strong>de</strong> uma suposição menos realista que a<br />
anterior. Veremos mais tar<strong>de</strong> como remover esta restrição e <strong>de</strong> que maneira<br />
é afectado o <strong>de</strong>sempenho global.<br />
17.3. As <strong>instruções</strong> <strong>de</strong> salto só usam dois andares: IF e ID.<br />
17.4. As <strong>instruções</strong> <strong>de</strong> escrita em memória (store) não têm WB.<br />
17.5. Estamos a assumir que as operações executadas em EX apenas necessitam<br />
<strong>de</strong> um ciclo <strong>de</strong> relógio. Isso não se aplica a operações como a divisão nem a<br />
operações com vírgula flutuante.<br />
17
Fundamentos da operação <strong>de</strong> pipelines Pipeline <strong>de</strong> 5 andares<br />
Múltiplos percursos <strong>de</strong> dados <strong>de</strong>sfasados no tempo<br />
O banco <strong>de</strong> registos tem <strong>de</strong> suportar 2 leituras e 1 escrita por ciclo.<br />
Problema: Salto condicional só modifica PC no andar ID.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 18/33<br />
18.1. Implicitamente estamos a assumir que existem recursos para executar<br />
<strong>concorrente</strong>mente as várias operações. Por exemplo, o cálculo do novo valor<br />
<strong>de</strong> PC no caso <strong>de</strong> um salto não po<strong>de</strong> ser feito pela ALU (que estará ocupada<br />
com o processamento <strong>de</strong> outra instrução).<br />
18.2. O banco <strong>de</strong> registos po<strong>de</strong> tornar-se facilmente num factor <strong>de</strong> contenção.<br />
Vamos assumir que todas as leituras são feitas no fim do ciclo <strong>de</strong> relógio (por<br />
<strong>instruções</strong> na fase ID) e que a escrita é feita no início do ciclo <strong>de</strong> relógio<br />
(pela instrução que passar para WB).<br />
período <strong>de</strong> relógio<br />
escrita<br />
18.3. Como estamos assumir uma implementação relativamente agressiva dos<br />
saltos condicionais, supomos que o PC é modificado no segundo ciclo da<br />
instrução (ID). Mesmo assim, os saltos (condicionais ou incondicionais) vão<br />
introduzir um conflito <strong>de</strong> controlo.<br />
18<br />
leitura
Fundamentos da operação <strong>de</strong> pipelines Pipeline <strong>de</strong> 5 andares<br />
Inserção <strong>de</strong> registos entre andares<br />
Evitam a interferência entre andares e preservam valores intermédios.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 19/33<br />
19.1. Sem cuidados especiais, a activida<strong>de</strong> “lógica” não po<strong>de</strong> ser limitada a um<br />
certo andar (i.e., os efeitos <strong>de</strong> qualquer mudança propagar-se-iam mais ou<br />
menos rapidamente por todo o circuito). Para evitar essa interferência, todos<br />
os os sinais que saem <strong>de</strong> um andar são “registados”, ou seja, os seus valores<br />
são guardados num elemento <strong>de</strong> memória (registo) cujo conteúdo apenas é<br />
alterado no flanco ascen<strong>de</strong>nte do relógio.<br />
19.2. Muitos dos registo já seriam necessários numa implementação multi-ciclo<br />
para preservar valores entre os diferentes ciclos.<br />
19.3. Os registos também serão aproveitados para evitar alguns conflitos<br />
provocados por <strong>de</strong>pendências <strong>de</strong> dados (cf. forwarding).<br />
19
Fundamentos da operação <strong>de</strong> pipelines Pipeline <strong>de</strong> 5 andares<br />
Desempenho <strong>de</strong> pipelines (1 a abordagem)<br />
Pipeline i<strong>de</strong>almente equilibrada: tinst_p = tinst<br />
Nandares<br />
Na prática, o tempo <strong>de</strong> execução <strong>de</strong> cada instrução aumenta<br />
ligeiramente <strong>de</strong>vido a overhead <strong>de</strong> controlo.<br />
O programa é mais rápido embora nenhuma instrução seja mais<br />
rápida!<br />
Speedup: Sp = tinst/tinst_p<br />
Na prática, certa <strong>instruções</strong> po<strong>de</strong>m necessitar <strong>de</strong> ciclos <strong>de</strong><br />
protelamento em algumas circunstâncias. Seja nprot o número médio<br />
<strong>de</strong> ciclos <strong>de</strong> protelamento por instrução. Então:<br />
CPIp = CPIi<strong>de</strong>al + nprot = 1 + nprot<br />
Assumindo igual frequência <strong>de</strong> relógio:<br />
Notas:<br />
Sp = CPI<br />
1 + nprot<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 20/33<br />
20.1. A melhoria <strong>de</strong> <strong>de</strong>sempenho proporcionada por pipelining é directamente<br />
<strong>de</strong>pen<strong>de</strong>nte do número médio <strong>de</strong> ciclos <strong>de</strong> protelamento. Torna-se, portanto,<br />
imperioso reduzir nprot ao mínimo. Como os saltos são, em muitas<br />
organizações <strong>de</strong> CPU, a principal fonte <strong>de</strong> ciclos <strong>de</strong> protelamento, torna-se<br />
vital reduzir a sua frequência ou o seu impacto.<br />
20.2. A expressão final para Sp assume que a frequência <strong>de</strong> relógio <strong>de</strong> uma<br />
implementação pipelined é igual à <strong>de</strong> uma implementação multi-ciclo. Na<br />
prática, será ligeiramente maior (≈ 10%). O número <strong>de</strong> <strong>instruções</strong><br />
executadas é naturalmente o mesmo.<br />
20
Resolução <strong>de</strong> conflitos<br />
Alguns problemas na utilização <strong>de</strong> pipelines<br />
Existem situações (hazards) em que uma instrução não po<strong>de</strong> ser executada<br />
durante o ciclo apropriado. Existem três tipos <strong>de</strong> conflitos:<br />
estruturais É um conflito <strong>de</strong> recursos: o hardware não suporta a<br />
execução <strong>concorrente</strong> <strong>de</strong> todas as combinações possíveis <strong>de</strong><br />
tarefas;<br />
<strong>de</strong> dados Este conflito surge quando uma instrução <strong>de</strong>pen<strong>de</strong> dos<br />
resultados <strong>de</strong> uma instrução anterior <strong>de</strong> uma forma que é<br />
“exposta” pela linha <strong>de</strong> processamento.<br />
<strong>de</strong> controlo este conflito resulta <strong>de</strong> saltos condicionais ou <strong>de</strong> outras<br />
<strong>instruções</strong> que alteram o PC.<br />
Conflitos reduzem o <strong>de</strong>sempenho i<strong>de</strong>al da pipeline.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 21/33<br />
21.1. Conflitos <strong>de</strong> dados e <strong>de</strong> controlo são <strong>de</strong>vidos, respectivamente, a<br />
<strong>de</strong>pendências <strong>de</strong> dados e <strong>de</strong> controlo. Conflitos estruturais resultam da<br />
inexistência <strong>de</strong> recursos suficientes para executar algumas operações<br />
<strong>concorrente</strong>mente.<br />
21
Resolução <strong>de</strong> conflitos<br />
Protelamento <strong>de</strong> operações<br />
Conflitos po<strong>de</strong>m obrigar a protelar (stall) o processamento <strong>de</strong> <strong>instruções</strong>.<br />
Habitualmente quando uma instrução é protelada:<br />
1. todas as <strong>instruções</strong> emitidas posteriormente também são proteladas;<br />
2. todas as <strong>instruções</strong> emitidas anteriormente <strong>de</strong>vem prosseguir (para<br />
“limpar” a situação <strong>de</strong> conflito).<br />
Consequência: durante o protelamento não são processadas novas<br />
<strong>instruções</strong>.<br />
Po<strong>de</strong> não ser economicamente proveitoso eliminar conflitos que ocorram<br />
raramente, porque os protelamentos associados são raros (número médio<br />
<strong>de</strong> ciclos <strong>de</strong> protelamento causados por esses conflitos é baixo).<br />
Nota: Existem esquemas em que as <strong>instruções</strong> emitidas posteriormente<br />
po<strong>de</strong>m “passar à frente” da instrução protelada (se não violarem nenhuma<br />
<strong>de</strong>pendência).<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 22/33<br />
22.1. O protelamento <strong>de</strong> operação é a solução “universal”. Contudo, é sempre<br />
preciso implementar circuitos <strong>de</strong> <strong>de</strong>tecção <strong>de</strong> conflitos (pipeline interlocks).<br />
22
Conflitos estruturais<br />
Resolução <strong>de</strong> conflitos Conflitos estruturais<br />
A execução <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong> em pipeline po<strong>de</strong> requerer que as<br />
unida<strong>de</strong>s funcionais sejam também pipelined e que outros recursos sejam<br />
duplicados para acomodar todas as combinações possíveis <strong>de</strong> <strong>instruções</strong>. O<br />
processamento po<strong>de</strong> exibir conflitos estruturais quando tal não acontece.<br />
Exemplo: processador com uma memória cache partilhada para<br />
dados/<strong>instruções</strong> (memória comum <strong>de</strong> dados e <strong>instruções</strong>).<br />
Ciclo <strong>de</strong> processador<br />
Instrução 1 2 3 4 5 6 7 8 9 10<br />
load IF ID EX MEM WB<br />
i + 1 IF ID EX MEM WB<br />
i + 2 IF ID EX MEM WB<br />
i + 3 stall IF ID EX MEM WB<br />
i + 4 IF ID EX MEM WB<br />
i + 5 IF ID EX MEM<br />
i + 6 IF ID EX<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 23/33<br />
23.1. Esta situação não ocorre na organização <strong>de</strong> base que estamos a analisar,<br />
porqie assumimos a existência <strong>de</strong> caches separadas <strong>de</strong> dados e <strong>instruções</strong>.<br />
23
Resolução <strong>de</strong> conflitos Conflitos estruturais<br />
Ilustração <strong>de</strong> um conflito estrutural<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 24/33<br />
24.1. Notar que não existe nenhum conflito estrutural associado ao banco <strong>de</strong><br />
registos.Por exemplo, no ciclo 5, ocorrem uma escrita no banco <strong>de</strong> registos<br />
(associado à instrução load) e duas leituras (associadas à instrução 3). Estas<br />
operações ocorrem em partes diferentes do ciclo, conforme indicado<br />
graficamente pelo tracejado: a escrita acontece no início do ciclo, as leituras<br />
no fim.<br />
24
Conflitos <strong>de</strong> dados<br />
Resolução <strong>de</strong> conflitos Conflitos <strong>de</strong> dados<br />
Um gran<strong>de</strong> efeito <strong>de</strong> pipelining é modificar a temporização relativa das<br />
operações, o que po<strong>de</strong> introduzir conflitos.<br />
Conflitos <strong>de</strong> dados ocorrem quando a or<strong>de</strong>m <strong>de</strong> leitura/escrita <strong>de</strong><br />
operandos é modificada (por referência à versão sem pipelining).<br />
Exemplo:<br />
DADD R1, R2, R3<br />
DSUB R4, R1, R5 ;ADD altera R1 em WB, mas DSUB requer valor em ID<br />
AND R6, R1, R7 ;R1 correcto fim do ciclo 5; AND lê registos no 4<br />
OR R8, R1, R9 ;OR não tem conflitos; leituras na 2 a parte do ciclo<br />
XOR R10, R1, R11 ;não tem conflitos<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 25/33<br />
25.1. O registos R1 é alterado pela primeira instrução. Como o resultado da<br />
instrução só atinge os registos no quinto ciclo, a instrução DSUB produz um<br />
conflito ao querer usar o novo valor <strong>de</strong> R1 no quarto ciclo (a sua etapa EX),<br />
já que isso implica a leitura dos registos no fim do ciclo anterior (o ciclo 3).<br />
25.2. A instrução AND também produz um conflito: precisa do novo valor logo no<br />
início do quinto ciclo (a sua etapa EX), e portanto, a leitura dos registos foi<br />
feita no ciclo anterior, quando o banco <strong>de</strong> registos ainda tinha o valor antigo<br />
<strong>de</strong> R1.<br />
25.3. A instrução OR já não provoca conflito.<br />
25.4. Este tipo <strong>de</strong> conflito entre <strong>instruções</strong> da ALU é muito frequente e introduz 2<br />
ciclos <strong>de</strong> protelamento, o que é muito. Consequentemente, este tipo <strong>de</strong><br />
conflito <strong>de</strong>ve necessariamente ser resolvido.<br />
25.5. Notar que o conflito surge porque a alteração do banco <strong>de</strong> registos é<br />
efectuada 2 ciclos <strong>de</strong>pois do cálculo do resultado. O resultado propriamente<br />
dito já foi calculado (no fim do ciclo 3).<br />
25
Resolução <strong>de</strong> conflitos Conflitos <strong>de</strong> dados<br />
Forwarding po<strong>de</strong> resolver conflitos <strong>de</strong> dados<br />
O resultado da ALU é realimentado para a sua entrada. O banco <strong>de</strong><br />
registos (leitura e escrita em partes diferentes do ciclo) também serve para<br />
fazer forwarding. A sequência analisada executa sem protelamento.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 26/33<br />
26.1. O novo valor <strong>de</strong> R1 é calculado antes <strong>de</strong> ser necessário para as operações<br />
seguintes. Apenas o banco <strong>de</strong> registos não é actualizado suficientemente<br />
<strong>de</strong>pressa.<br />
26.2. Para resolver o conflito, basta “agulhar” o futuro valor <strong>de</strong> R1 do registo<br />
temporário on<strong>de</strong> se encontra para a entrada do andar que necessita do seu<br />
valor. Locais <strong>de</strong> origem e <strong>de</strong>stino dos dados estão indicados a tracejado na<br />
figura.Sempre que o <strong>de</strong>stino estiver à frente da origem, <strong>de</strong>ve proce<strong>de</strong>r-se ao<br />
agulhamento necessário.<br />
26.3. Para além dos circuitos <strong>de</strong> agulhamento (basicamente multiplexadores), o<br />
CPU <strong>de</strong>ve dispor também <strong>de</strong> circuitos <strong>de</strong> <strong>de</strong>tecção dos conflitos.<br />
26.4. A utilização <strong>de</strong> forwarding é indispensável para um bom <strong>de</strong>sempenho.<br />
26
Resolução <strong>de</strong> conflitos Conflitos <strong>de</strong> dados<br />
Protelamento po<strong>de</strong> ser inevitável<br />
LD R1, 0(R2) ; dados prontos apenas no fim do ciclo 4<br />
DSUB R4, R1, R5 ; necessita <strong>de</strong> dados no início <strong>de</strong> 4<br />
AND R6, R1, R7<br />
OR R8, R1, R9<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 27/33<br />
27.1. O esquema <strong>de</strong> forwarding não resolve todos os problemas. Por exemplo, o<br />
conflito que ocorre quando uma instrução usa um valor lido <strong>de</strong> memória pela<br />
instrução imediatamente prece<strong>de</strong>nte não po<strong>de</strong> ser resolvido <strong>de</strong>sta maneira.<br />
27.2. De facto, o valor ainda não está no CPU no instante em que se preten<strong>de</strong>ria<br />
utilizá-lo. Isso é indicado pelo segmento a tracejado em que o <strong>de</strong>stino (o<br />
“lugar” on<strong>de</strong> o valor é necessário para o restante processamento) está<br />
“atrás” da origem.<br />
27
Resolução <strong>de</strong> conflitos Conflitos <strong>de</strong> dados<br />
Protelar resolve o problema. . .<br />
Ciclo <strong>de</strong> processador<br />
Instrução 1 2 3 4 5 6 7 8 9<br />
LD R1,0(R2) IF ID EX MEM WB<br />
DSUB R4, R1, R5 IF ID EX MEM WB<br />
AND R6, R1, R7 IF ID EX MEM WB<br />
OR R8, R1, R9 IF ID EX MEM WB<br />
LD R1,0(R2) IF ID EX MEM WB<br />
DSUB R4, R1, R5 IF ID stall EX MEM WB<br />
AND R6, R1, R7 IF stall ID EX MEM WB<br />
OR R8, R1, R9 stall IF ID EX MEM WB<br />
A sequência <strong>de</strong> etapas <strong>de</strong> processamento da tabela inferior mostra como o<br />
protelamento resolve o problema <strong>de</strong> ter os dados correctos em R1.<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 28/33<br />
28.1. Notar que o processamento <strong>de</strong> todas as <strong>instruções</strong> subsequentes à que<br />
provoca o conflito também são proteladas.<br />
28.2. A obtenção <strong>de</strong> <strong>instruções</strong> (etapa IF) também é protelada.<br />
28
Conflitos <strong>de</strong> controlo<br />
Resolução <strong>de</strong> conflitos Conflitos <strong>de</strong> controlo<br />
Saltos condicionais causam conflitos <strong>de</strong> controlo porque po<strong>de</strong>m alterar ou<br />
não o PC (quando o fazem é no fim <strong>de</strong> ID).<br />
O que fazer enquanto não é possível <strong>de</strong>terminar se o salto é realmente<br />
feito (ou tomado) ? Nada. . . e repetir quando já existir a informação.<br />
Ciclo <strong>de</strong> processador<br />
Instrução 1 2 3 4 5 6 7 8 9<br />
salto relativo IF ID EX MEM WB<br />
sucessor IF IF ID EX MEM WB<br />
sucessor + 1 IF ID EX MEM WB<br />
sucessor + 2 IF ID EX MEM WB<br />
Dada a frequência <strong>de</strong> saltos relativos, estes conflitos po<strong>de</strong>m levar a perdas<br />
<strong>de</strong> <strong>de</strong>sempenho na or<strong>de</strong>m dos 10%–30% !<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 29/33<br />
29.1. Nesta arquitectura um salto condicional <strong>de</strong>mora tanto como um salto<br />
incondicional. Em ambos os caso, o PC tem o novo valor do en<strong>de</strong>reço no fim<br />
da etapa ID.<br />
29.2. Em muitas organizações <strong>de</strong> pipeline, o salto condicional necessita <strong>de</strong> mais<br />
ciclos que o incondicional.<br />
29.3. A instrução “sucessor” não po<strong>de</strong> entrar imediatamente na fase <strong>de</strong><br />
<strong>de</strong>scodificação. O primeiro IF (da 2 a linha) obtém a instrução do valor<br />
“regular” do PC (a instrução resi<strong>de</strong>nte em memória a seguir ao salto<br />
relativo). Este valor po<strong>de</strong> não ser o correcto. O segundo IF já usa o valor <strong>de</strong><br />
PC <strong>de</strong>finido no ciclo 2 (o valor correcto).<br />
29.4. Quando o salto não é tomado, o segundo IF é <strong>de</strong>snecessário. Esse facto po<strong>de</strong><br />
ser aproveitado para reduzir o impacto do conflito <strong>de</strong> controlo.<br />
29
Resolução <strong>de</strong> conflitos Conflitos <strong>de</strong> controlo<br />
Alternativa: Optar por uma das alternativas<br />
O CPU assume que o salto não é tomado e corrige a sua <strong>de</strong>cisão quando<br />
<strong>de</strong>terminar o valor da condição (se necessário).<br />
Ciclo <strong>de</strong> processador<br />
Instrução 1 2 3 4 5 6 7 8 9<br />
salto não tomado IF ID EX MEM WB<br />
i + 1 IF ID EX MEM WB<br />
i + 2 IF ID EX MEM WB<br />
i + 3 IF ID EX MEM WB<br />
i + 4 IF ID EX MEM WB<br />
salto tomado IF ID EX MEM WB<br />
i + 1 IF inact. inact. inact. inact.<br />
alvo IF ID EX MEM WB<br />
alvo + 1 IF ID EX MEM WB<br />
alvo + 2 IF ID EX MEM WB<br />
Também se po<strong>de</strong> partir do princípio que o salto é tomado. No caso<br />
presente, isso não tem interesse. (Porquê?)<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 30/33<br />
30.1. Assumir que o salto é tomado obriga ainda assim a calcular a posição da<br />
instrução seguinte (“alvo” do salto).<br />
30.2. Quando o salto é tomado, a instrução subsequente em processamento é<br />
errada: internamente é transformado numa instrução NOP (no operation).<br />
No nosso caso, isso é simples, porque a instrução ainda não alterou o estado<br />
interno do CPU.<br />
30
Resolução <strong>de</strong> conflitos Conflitos <strong>de</strong> controlo<br />
<strong>Execução</strong> incondicional da instrução seguinte<br />
Outra opção: Executar sempre a instrução colocada a seguir ao salto!<br />
Ciclo <strong>de</strong> processador<br />
Instrução 1 2 3 4 5 6 7 8 9<br />
salto não tomado IF ID EX MEM WB<br />
(<strong>de</strong>lay slot) i + 1 IF ID EX MEM WB<br />
i + 2 IF ID EX MEM WB<br />
i + 3 IF ID EX MEM WB<br />
i + 4 IF ID EX MEM WB<br />
salto tomado IF ID EX MEM WB<br />
(<strong>de</strong>lay slot) i + 1 IF ID EX MEM WB<br />
alvo IF ID EX MEM WB<br />
alvo + 1 IF ID EX MEM WB<br />
alvo + 2 IF ID EX MEM WB<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 31/33<br />
31.1. A este tipo <strong>de</strong> salto chama-se <strong>de</strong>layed btanch e a posição da instrução<br />
executada incondicionalmente <strong>de</strong>signa-se por <strong>de</strong>lay slot. Algumas<br />
arquitecturas têm dois <strong>de</strong>lay slots.<br />
31.2. Neste caso, o trabalho <strong>de</strong> encontrar uma instrução para o <strong>de</strong>lay slot é<br />
<strong>de</strong>ixado ao compilador ou programador em assembly.<br />
31.3. Caso não seja possível encontrar uma instrução apropriada, coloca-se uma<br />
instrução NOP no <strong>de</strong>lay slot.<br />
31
Resolução <strong>de</strong> conflitos Conflitos <strong>de</strong> controlo<br />
Branch <strong>de</strong>lay slot: estratégias <strong>de</strong> sequenciamento<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 32/33<br />
32.1. A situação (a) é a melhor. As estratégias (b) e (c) são usadas quando a<br />
estratégia (a) não é possível.<br />
32.2. A estratégia (b) requer geralmente a duplicação da instrução colocada no<br />
<strong>de</strong>lay slot. Esta estratégia é boa para casos em que o salto é tomado com<br />
elevada probabilida<strong>de</strong> (como no caso dos ciclos).<br />
32.3. Para que (b) e (c) sejam legais, é necessário que a execução da instrução não<br />
altere o resultado do programa. Quando muito, o trabalho executado por<br />
essa instrução é <strong>de</strong>sperdiçado.<br />
32.4. No caso do exemplo (c), o registo R7 po<strong>de</strong>ria conter um valor temporário<br />
que simplesmente não é usado quando o salto é tomado. Note-se que o caso<br />
(b) o valor final <strong>de</strong> R4 não é o mesmo; se isso for importante, a duplicação<br />
da instrução não po<strong>de</strong> ser feita.<br />
32
Resolução <strong>de</strong> conflitos Conflitos <strong>de</strong> controlo<br />
Impacto da arquitectura do conjunto <strong>de</strong> <strong>instruções</strong><br />
A arquitectura do conjunto <strong>de</strong> <strong>instruções</strong> po<strong>de</strong> introduzir factores <strong>de</strong><br />
ineficiência e dificulda<strong>de</strong>s <strong>de</strong> implementação.<br />
Instruções que que alteram o estado a “meio” da execução dificultam<br />
o tratamento <strong>de</strong> excepções. Exemplos: auto-incremento em IA-32. O<br />
processador necessita <strong>de</strong> ter a capacida<strong>de</strong> para reverter o estado.<br />
Instruções <strong>de</strong> cópia <strong>de</strong> strings também pertencem a esta categoria<br />
(solução: usar registos como memória <strong>de</strong> trabalho).<br />
Alguns elementos <strong>de</strong> estado (p. ex. códigos <strong>de</strong> condição) po<strong>de</strong>m<br />
dificultar o funcionamento da pipeline (p. ex. <strong>instruções</strong> que alterem<br />
os c.c. não po<strong>de</strong>m ser colocadas no <strong>de</strong>lay slot). Po<strong>de</strong> ser difícil<br />
<strong>de</strong>terminar quando é que o código <strong>de</strong> condição é válido.<br />
Instruções multi-ciclo introduzem gran<strong>de</strong>s <strong>de</strong>sequilíbrios (<strong>instruções</strong><br />
po<strong>de</strong>m <strong>de</strong>morar <strong>de</strong> 1 a >100 ciclos). Solução: pipeline <strong>de</strong><br />
microcódigo (VAX8800, IA-32 <strong>de</strong>pois <strong>de</strong> 1995).<br />
Notas:<br />
c○JCF, 2004 — AAC (FEUP/LEIC) <strong>Execução</strong> <strong>concorrente</strong> <strong>de</strong> <strong>instruções</strong>: <strong>Pipelines</strong> 33/33<br />
33.1. Na arquitectura IA-32, a utilização modos <strong>de</strong> en<strong>de</strong>reçamento com<br />
auto-incremento implica a actualização <strong>de</strong> registos a meio da execução do<br />
programa.<br />
33.2. Durante muito tempo, partiu-se do princípio que a interacção entre a<br />
arquitectura do conjunto <strong>de</strong> <strong>instruções</strong> e a implementação era reduzida, pelo<br />
que s questões <strong>de</strong> implementação não tinham peso na concepção dos<br />
conjuntos <strong>de</strong> <strong>instruções</strong>. A partir dos anos 80 tornou-se claro que a<br />
dificulda<strong>de</strong> <strong>de</strong> implementar pipelines e a sua ineficiência po<strong>de</strong>m ser<br />
aumentadas por más escolhas a nível da ACI.<br />
33