Skip to content

Commit

Permalink
refactor: fix typos and clarify stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
diogotcorreia committed Nov 12, 2023
1 parent e628ab6 commit f09d908
Showing 1 changed file with 32 additions and 24 deletions.
56 changes: 32 additions & 24 deletions content/oc/0006-pipelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ tempo para executar uma instrução não é reduzido.
A Pipeline do MIPS tem [5 etapas](color:pink) (stages):

- **[IF](color:green): Instruction Fetch** - a instrução é lida
- **[ID](color:green): Instruction Decode** - leitura de registos involvidos
- **[EX](color:green): Execute** - a operação aritemética é executada ou o endereço é calculado
- **[ID](color:green): Instruction Decode** - leitura de registos envolvidos
- **[EX](color:green): Execute** - a operação aritmética é executada ou o endereço é calculado
- **[MEM](color:green): Memory** - acesso à memória
- **[WB](color:green): Write-Back** - escrever o resultado num registo

Expand All @@ -43,15 +43,15 @@ várias instruções (com diferentes fases) simultaneamente. Assim que a pipelin
é completada a cada ciclo, o que nos dá um CPI de 1. Se todas as etapas tiverem balanceadas, i.e. demorarem todas o mesmo tempo:

$$
TimeInstructions_{pipelined} = \frac{TimeInstructions_{non pipelined}}{Number of Stages}
\text{TimeInstructions}_{\text{pipelined}} = \frac{\text{TimeInstructions}_{\text{non pipelined}}}{\text{Number of Stages}}
$$

## ISA do MIPS & Pipeline

O Instruction-Set Architecture (**ISA**) do MIPS foi desenhado para pipelining. Logo:

1. Todas as instruções são de 32-bits - mais fácil fazer fetch e decode num ciclo.
2. Existem poucos formatos de instrução - mais facil fazer decode e ler registos numa etapa.
1. Todas as instruções são do mesmo tamanho (32-bits) - mais fácil fazer fetch e decode num ciclo.
2. Existem [poucos formatos de instrução](/oc/linguagem-computador/#categorias-de-instruções) - mais fácil fazer decode e ler registos numa etapa.
3. Apenas ocorrem operações de memória em _Loads_ e _Stores_ - podemos usar o passo de execução para calcular endereços de memória.
4. Cada instrução escreve no máximo 1 resultado - nos últimos andares da pipeline (MEM ou WB)
5. Operandos têm que estar alinhados em memória - uma transferência de dados leva apenas a um acesso à memória de dados
Expand All @@ -66,20 +66,23 @@ chamamos **[Pipeline Hazards](color:yellow)** e existem 3 tipos:

- **[Data Hazards](color:green)**: a instrução seguinte depende do resultado da instrução anterior.

- **[Control Hazards](color:green)**: uma decisão pode ser tomada antes da condição ter sido avaliada por uma instrução anterior (como acontece nos _Branches_).
- **[Control Hazards](color:green)**: uma decisão pode ser tomada antes da condição ter sido
avaliada por uma instrução anterior (como acontece nos _branches_).

Normalmente, estes problemas conseguem ser resolvidos através de _stalls_. A unidade de controlo
Normalmente, estes problemas conseguem ser resolvidos através de _stalls_, isto é, a introdução de um atraso
entre a execução de duas instruções, de forma a evitar estes _hazards_. No entanto, _stalls_ são indesejados
dado que aumentam o tempo de execução e diminuem a eficiência do processador. A unidade de controlo
da pipeline é responsável por detetar estes problemas e resolvê-los.

## Structural Hazards

Quando há conflito no uso de um recurso. Por exemplo no caso da pipeline do MIPS com uma única
memória: instruções _Load_/_Store_ acedem a dados, pelo que o fetch da instrução deveria
ter que usar um "stall" para esse ciclo.
memória: instruções _Load/Store_ acedem a dados, pelo que o fetch da instrução deveria
ter que usar um _stall_ para esse ciclo.

## Data Hazards

Uma instrução depende do acesso a dados realizado por uma instrucão anterior. Existem vários tipos
Uma instrução depende do acesso a dados realizado por uma instrução anterior. Existem vários tipos
de data hazards mas o mais abordado na cadeira é o _RAW_ (Read After Write), onde uma instrução
tenta ler um registo que ainda não foi escrito por outra anterior.

Expand All @@ -97,11 +100,11 @@ Olhemos para o seuinte exemplo:

![Data Hazard](./assets/0006-dataforwarding.png#dark=1)

Na primeira instrução, o valor do registo \$s0 irá ser determinado no andar EX, como em
qualquer outra instrução aritmética. Se forwarding não fosse usado teriamos de esperar
até que este valor fosse escrito no registo \$s0, no andar WB, para que a segunda instrução
podesse usar esse valor, o que nos obrigaria a usar _stalls_ que [degradariam a performance](color:pink)
do CPU. Em vez disso podemos simplesmente propagar o valor (fazer forwarding) logo após este
Na primeira instrução, o valor do registo `$s0` irá ser determinado no andar EX, como em
qualquer outra instrução aritmética. Se forwarding não fosse usado, teríamos de esperar
até que este valor fosse escrito no registo `$s0`, no andar WB, para que a segunda instrução
pudesse usar esse valor, o que nos obrigaria a usar _stalls_ que [degradariam a performance](color:pink)
do CPU. Em vez disso podemos simplesmente propagar o valor (fazer _forwarding_) logo após este
ser calculado, partilhando-o entre etapas.

É importante ter em conta que apesar de bastante útil, esta sofisticação pode não ser
Expand All @@ -110,9 +113,11 @@ anteriormente ao ciclo em que o valor foi calculado.

![Exemplo de Data Forwarding](./assets/0006-dataforwardingeg.png#dark=1)

:::tip[Exemplos]
:::info[Exemplos]

É recomendado a resolução da ficha prática de Pipelining para melhor compreensão do conceito de data forwading.
É recomendado a resolução da ficha prática de _pipelining_ para melhor compreensão do conceito de _data forwading_.

<!-- TODO add examples -->

:::

Expand All @@ -121,8 +126,8 @@ anteriormente ao ciclo em que o valor foi calculado.
Um branch determina o fluxo de controlo, sendo necessário esperar pelo seu resultado.
Nem sempre o pipeline consegue fazer fetch da instrução correta. Existem varias soluções:

- **Stalling**: lento e forte impacto negativo no CPI
- Fazer a decisão o mais cedo possível no pipeline (reduzindo ciclos afetados por stall)
- _**Stalling**_: lento e forte impacto negativo no CPI
- Fazer a decisão o mais cedo possível no pipeline (reduzindo ciclos afetados por _stall_)
- Adiar a decisão (necessário apoio do compilador)
- **Prever o resultado** (e esperar que corra bem)

Expand All @@ -133,7 +138,8 @@ todos os branch stalls com **[delayed branches](color:pink)**. Com esta técnica
executamos sempre a próxima instrução sequencial depois da instrução branch,
sendo que o branch só é efetuado após essa instrução.

Com pipelines maiores, o branch delay começou a precisar de mais que um slot.
Em processadores mais sofisticados, com pipelines maiores, o branch delay começou
a precisar de mais que um slot.

### Branch Prediction

Expand All @@ -143,16 +149,18 @@ o que permite ir buscar a instrução a seguir ao branch sem delay. Caso, afinal
seja efetuado, é necessário fazer **[flush](color:yellow)** das instruções que entretanto
tinham sido entretanto feitas, i.e. substituí-las por um _nop_.

### Static Branch Prediction
Existem dois tipos de _branch prediction_.

#### Static Branch Prediction

Os control hazards são rresolvidos assumindo sempre um dado resultado e procedendo sem
Os control hazards são resolvidos assumindo sempre um dado resultado e procedendo sem
esperar para ver o resultado final. Os dois tipos de Static Prediction mais comuns são:

**Predict Branch Not Taken** - assume-se sempre que [o branch não é tomado](color:pink) (é feito flush caso seja tomado)

**Predict Branch Taken** - assume-se sempre que [o branch é tomado](color:pink) (é feito flush se não for tomado)

### Dynamic Branch Prediction
#### Dynamic Branch Prediction

O hardware mede o comportamento do branch, tendo em conta o seu historial das últimas decisões.
Assume que no futuro, o comportamento se vai manter, atualizando o historial quando estiver errado
Expand All @@ -165,7 +173,7 @@ ocorram duas escolhas erradas sucessivas.

## Exceções e Interrupções

Até agora vimos a utilidade do _Pipelining_ e as diversas formas que temos de
Até agora vimos a utilidade do _pipelining_ e as diversas formas que temos de
[resolver dependências entre instruções](color:yellow). Mas é imperativo que quando surjam
eventos inesperados na execução, estes sejam tratados pelo CPU. Estes eventos podem ser geralmente
categorizados em duas categorias:
Expand Down

0 comments on commit f09d908

Please sign in to comment.