Skip to content

Commit

Permalink
[OC] Fix virtual memory content (#876)
Browse files Browse the repository at this point in the history
Co-authored-by: Diogo Correia <me@diogotc.com>
  • Loading branch information
Pesteves2002 and diogotcorreia committed Nov 13, 2023
1 parent 6750a16 commit ccf9156
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 67 deletions.
40 changes: 24 additions & 16 deletions content/oc/0004-memoria-caches.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ pequena de espaço de memória a qualquer momento durante a execução.
| mais caro | mais barato |
| estático, o conteúdo fica para "sempre" enquanto estiver ligado | dinâmico, precisa de ser "refrescado" regularmente (a cada 8 ms); consome 1% a 2% dos ciclos ativos da DRAM |

Para além disso, os endereços da DRAM são [divídido em 2 metades](color:pink), linha e coluna:
Para além disso, os endereços da DRAM são [dividido em 2 metades](color:pink), linha e coluna:

- [RAS (_Row Access Strobe_)](color:orange) que aciona o decodificador de linha;
- [CAS (_Columm Access Strobe_)](color:green) que aciona o selecionador de coluna.
- [CAS (_Column Access Strobe_)](color:green) que aciona o selecionador de coluna.

### Como Funciona a Hierarquia de Memória?

Expand Down Expand Up @@ -130,7 +130,7 @@ Se tentarmos aceder a dados e estes estiverem disponíveis no nível mais alto d
temos um [**_hit_**](color:green), caso contrário teremos um [**_miss_**](color:red)
e teremos de ir buscar os dados a um nível inferior.

Podemos ainda calcular o [**_hit rate_**](color:purple) de multiplos acessos através da seguinte fórmula:
Podemos ainda calcular o [**_hit rate_**](color:purple) de múltiplos acessos através da seguinte fórmula:

$$
\op{\text{Hit Rate}}
Expand Down Expand Up @@ -305,6 +305,14 @@ Existem três tipos de [cache misses](color:pink):
são mapeados para o mesmo set ou _block frame_, num set de associatividade ou até mesmo
em posicionamento de blocos em mapeamento direto.

### Cache Design Trade-offs

| Alteração | Efeito na _miss rate_ | Efeito negativo de _performance_ |
| :---------------------------------- | :-------------------------------- | :---------------------------------------------------------------------------------------- |
| Aumento do tamanho da cache | Diminuição de _capacity misses_ | Aumento do tempo de acesso |
| Aumento da associatividade da cache | Diminuição de _conflict misses_ | Aumento do tempo de acesso |
| Aumento do _block size_ | Diminuição de _compulsory misses_ | Aumento do _miss penalty_. Para blocos muito grandes, pode aumentar também a _miss rate_. |

## _Write-Policies_

Uma [_write policy_ da cache](color:pink) refere-se a um comportamento da cache
Expand All @@ -316,7 +324,7 @@ policies_.
### _Write-through_

Quando nos deparamos com um _data-write hit_ podemos somente atualizar o bloco da
cache, mas isto implicaria que a cache e a memória se tornassem insconsistentes. Temos,
cache, mas isto implicaria que a cache e a memória se tornassem inconsistentes. Temos,
por isso, que introduzir a política [_write-through_](color:pink): esta, sendo a mais
fácil de implementar, basicamente atualiza os dados tanto em cache como em memória ao
mesmo tempo. É usada quando não há escritas muito frequentes na cache e ajuda com a
Expand Down Expand Up @@ -377,7 +385,7 @@ diferentes:
## Medir a Performance da Cache

Para conseguirmos perceber se uma cache está a funcionar bem ou não, temos que saber
os termos involvidos nestes cálculos:
os termos envolvidos nestes cálculos:

- [Bloco ou linha](color:yellow): a unidade mínima de informação que está presente, ou
não, na cache;
Expand All @@ -393,7 +401,7 @@ os termos involvidos nestes cálculos:

Assim, como já sabíamos, os componentes do tempo do CPU são os [ciclos de execução do programa](color:purple),
que includem o tempo de _cache-hit_ e os [ciclos de atraso de memória](color:purple)
que provêm maioritariamente de _cache misses_. Assumindo que o sucesso de cache incui a
que provêm maioritariamente de _cache misses_. Assumindo que o sucesso de cache inclui a
parte normal dos ciclos de execução do programa, então podemos calcular o tempo de CPU
através da seguinte fórmula:

Expand Down Expand Up @@ -431,7 +439,7 @@ como um _miss penalty_ de 100 ciclos, uma [base CPI](color:yellow), ou seja, cac
ideal, igual a 2, e [loads e stores](color:purple) que ocupam 36% das instruções.

A primeira coisa que pretendemos calcular são os _miss cycles_ por instrução, que, como
vimos aicma, são dados pelo _miss rate_ e o _miss penalty_. Temos, por isso:
vimos acima, são dados pelo _miss rate_ e o _miss penalty_. Temos, por isso:

$$
\text{miss cycle} = \text{miss rate} \times \text{miss penalty}
Expand All @@ -456,11 +464,11 @@ Por isso. o CPU ideal é $$5.44/2=2.72$$ vezes mais rápido

### Ideias a Reter

Desta forma, concluimos que quando a performance do CPU aumenta, o _miss penalty_
Desta forma, concluímos que quando a performance do CPU aumenta, o _miss penalty_
[diminui](color:purple); uma diminuição baseada no CPI implica uma maior [proporção de tempo](color:purple)
gasto em atrasos na memória; e, um aumento de _clock rate_ significa que os atrasos de
memória contam para mais [ciclos de CPU](color:purple). Não podemos, evidentemente
negligenciar o comportamneto da cache quando estamos a avaliar a perfomance do sistema.
negligenciar o comportamento da cache quando estamos a avaliar a performance do sistema.

## Reduzir os _Miss Rates_ na Cache

Expand All @@ -472,7 +480,7 @@ um bloco de memória seja mapeado para qualquer bloco de cache numa [cache total

Por isso mesmo, o compromisso é dividir a cache em **sets** que contêm _n_ formas
([_n-way set associative_](color:pink)). Os blocos de memória mapeiam um _set_ único,
específicado pelo campo de index, e pode ser posto em qualquer sítio desse _set_, é por
especificado pelo campo de index, e pode ser posto em qualquer sítio desse _set_, é por
isso que há _n_ escolhas.

| [Totalmente associativo](color:yellow) | [Associatividade com _n_ sets](color:orange) |
Expand All @@ -494,19 +502,19 @@ encontra presente. Como não se encontra, temos um _miss_ e escrevemos na primei
o valor 0. Já tendo o 0 em cache, seguimos para o 4, vamos à primeira linha e vemos se
está na cache, contudo na cache só está o 0, logo, há um miss. Como estamos a tratar do
número 4, contamos as linhas até chegarmos à linha 4, como só há 4 linhas no total e
começamos a contar do zero, temos que modficar a nossa primeira linha para passar a ter
começamos a contar do zero, temos que modificar a nossa primeira linha para passar a ter
a informação relevante ao 0 para ter informação relevante ao 4. Assim, na nossa
primeira linha agora temos o 4.

Acabando os dois primeiros valores, passamos ao 0, vamos à primeira linha, vemos que já
está preenchida, há _miss_ e temos que voltar a preencher com o 0. Reparamos, por isso,
que temos o mesmo caso que no início do nosso exercício. Ora, como o resto dos valores
são sempre ou 0 ou 4 intrecalando, vamos sempre ter um _miss_ e vamos sempre ter que
são sempre ou 0 ou 4 intercalando, vamos sempre ter um _miss_ e vamos sempre ter que
voltar a preencher a primeira linha com o valor 0 ou 4.

![Exemplo](./assets/0004-exemplo.jpg#dark=3)

Porém, se tivessemos a ver a mesma string mas numa [2 way set associative_cache](color:pink),
Porém, se tivéssemos a ver a mesma string mas numa [2 way set associative_cache](color:pink),
só teríamos _miss_ nos primeiros dois acessos, visto que os nossos valores seriam
guardados na primeira e segunda via da primeira linha, e a partir daí conseguiríamos
aceder aos endereços 0 e 4 com sucesso.
Expand All @@ -530,7 +538,7 @@ dar acesso a novos? Em [mapeamento direto](color:purple) não temos escolha, con
[set de associatividade](color:purple) geralmente vemos primeiro se há alguma
[entrada não válida](color:pink), se não escolhemos a entrada que não está a ser usada
há mais tempo (LRU- _least-recently used_), o que é simples para uma via de 2, é gerível
com facilidade para 4, mas mais do que isso é demasiado díficil; nesses casos
com facilidade para 4, mas mais do que isso é demasiado difícil; nesses casos
funciona mais ou menos da mesma forma mas mais [aleatório](color:pink).

:::tip[Exemplos]
Expand Down Expand Up @@ -652,7 +660,7 @@ Existem várias técnicas para otimização de acesso de dados:
Pré-busca, [_prefetching_](color:blue) em inglês, refere-se ao carregamento de um
recurso antes que seja necessário de modo a diminuir o tempo de espera para esse recurso. Assim, um [_software prefetching_](color:pink) não pode ser feito nem
demasiado cedo, visto que os dados podem ser expulsos antes de serem usados, nem
demasiado tarde pois os dados podem não der trazidos a tempo da sua utilizção. Esta
demasiado tarde pois os dados podem não der trazidos a tempo da sua utilização. Esta
técnica é [_greedy_](color:purple). Da mesma forma, o pré-carregamento, ou
[_preloading_](color:blue) em inglês funciona como um pseudo prefetching, usando um
processamento _hit-under-miss_, isto é o acesso antigo é um _miss_ e, por isso, o
Expand Down Expand Up @@ -691,7 +699,7 @@ no esquema abaixo.
Em 1989, McFarling [reduziu _cache misses_ por 75%](color:pink) em caches de mapeamento
direto de 8 kB e blocos de 4 bytes em software. Para tal, foi necessário
[reorganizar os procedimentos em memória](color:purple) para reduzir os
_conflict misses_, assim como fazer um [perfil](color:purple) de modo a analizar os
_conflict misses_, assim como fazer um [perfil](color:purple) de modo a analisar os
conflitos, usando ferramentas que eles desenvolver. Os dados obtidos foram os seguintes:

- [_Merging arrays_](color:yellow): melhoram a localidade espacial através de um único
Expand Down
80 changes: 29 additions & 51 deletions content/oc/0005-memoria-virtual.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,6 @@ type: content

# Memória Virtual

:::danger[Conteúdo Não Revisto]

O conteúdo abaixo não foi revisto e poderá conter erros.
Agradecem-se [contribuições](https://docs.leic.pt/).

Apenas foi aqui incluído devido à proximidade do MAP45 dia 2022/10/20.

:::

```toc
```
Expand All @@ -32,7 +23,7 @@ sistema operativo (OS). Assim, cada programa é compilado para o seu próprio es
endereçamento, um [espaço de endereçamento virtual](color:purple). Nos programas que
partilham a memória principal, cada um tem direito a um espaço de endereçamento físico
privado que é frequentemente usado para código e dados, assim como é protegido dos
outros programas. O CPU e o OS traduzem o endereço virtual para um enderço físico,
outros programas. O CPU traduz o endereço virtual para um enderço físico,
tendo, por isso, a VM um "bloco" que se chama [página](color:pink) e em vez de
dizermos que houve um _"miss"_ dizemos que houve uma [_page fault_](color:pink).

Expand All @@ -57,24 +48,21 @@ Sempre que a cache acomoda um subcojunto de posições da memória principal, a
principal acomoda um subconjunto de posições da memória virtual. Cada
[bloco corresponde a uma página](color:pink). A [dimensão](color:pink) é geralmente
bastante elevada de modo a aumentar a eficiência quando o disco é acedido, e também
reduz a dimensão da tabela de tradução; porém, quanto maior for, maior é a potencial
perda de memória (em média, 50% da dimensão da página); valores típicos são entre 4K
e 8K bytes.
reduz a dimensão da tabela de tradução; porém, quanto maior for a página, maior é a potencial
perda de memória (em média, 50% da dimensão da página); valores típicos de página são os 4
ou 8 Kbytes.

Um bloco pode ser posicionado em qualquer [espaço de memória](color:purple) visto ter
associatividade total.
Uma página pode ser posicionada em qualquer [espaço de memória](color:purple) visto a memória principal ter associatividade total.

![Tradução usando tabela de página](./assets/0005-traducao2.png#dark=3)

## Tabelas de Página
## Tabelas de Páginas

As [tabelas de página](color:pink) guardam o posicionamento da informação num array
te entradas todas indexadas por um número de uma página virtual. A tabela de página
regista os pontos do CPU da tabela de página em [memória física](color:purple). Se
uma página [estiver presente em memória](color:orange), temos o PTE
(_Page Table Entry_) que guarda o número da página física assim como outros bits de
estado (_dirty_, _referenced_, ...); caso contrário, o [PTE](color:pink) pode referir
outra localização em troca de espaço no disco.
As [tabelas de páginas](color:pink) guardam o posicionamento da informação num array
de entradas todas indexadas pelo número da página virtual. A tabela de páginas
regista o endereço de memória física para onde aponta a página do endereço virtual
além de outros bits de estado (_dity_, _referenced_, ...);
caso contrário, o [PTE](color:pink) pode referir outra localização em troca de espaço no disco.

### Trocas e Escritas

Expand All @@ -94,8 +82,8 @@ quando a página é escrita.
Um problema comum na paginação de sistemas é que a dimensão de uma tabela de página
não é obrigada a traduzir os endereços: apenas precisa de alocar numa
[região contida](color:pink) a memória física. Por exemplo, se temos um espaço virtual
com $2^{32}$ bytes e páginas com 4 bytes, ou seja $2^{12}$, temos uma tabela com
$2^{20}$ entradas visto que $32-12=20$, ou seja temos 4M bytes de entradas!
com $2^{32}$ bytes e páginas com 4 kbytes, ou seja $2^{12}$ bytes, temos uma tabela com
$2^{20}$ entradas visto que $32-12=20$, ou seja temos 1 milhão entradas!

### Hierarquia das Tabelas de Página

Expand All @@ -108,14 +96,14 @@ hierarquia da tradução das tabelas.
:::tip[Exemplos]

Para mais exemplos de cálculos com hierarquia de memória é recomendada a
realização da quinta ficha das aulas práticas ou ver a sua resolução.
realização da ficha das aulas práticas ou ver a sua resolução.

:::

## Tabelas Invertidas

A tradução de endereços é baseada em [_hash tables_](color:purple). Uma qualquer
função has H(x) é aplicada ao endereço virtual de modo a encontrar uma fila
função hash H(x) é aplicada ao endereço virtual de modo a encontrar uma fila
particular de descriptores composta pelos pares [página virtual - página física](color:pink)
, que correspondem a endereços virtuais dando origem ao mesmo valor da função hash H(x)
em termos de colisões. Assim, o endereço físico necessário pode, ou não, estar presente
Expand All @@ -132,26 +120,18 @@ Assim, é necessária [memória extra](color:blue) para aceder a tradução de V
Isto faz com que os acessos à memória sejam ainda mais caros, e a maneira de resolver
o problema de _hardware_ é através de um [_Translation Lookaside Buffer_](color:pink)
isto é, uma **TLB**, que corresponde a uma cache mais pequena que acompanha os endereços
acedidos de modo a evitar ter que fazer uma tabela de página para os encontrar.
acedidos de modo a evitar ter que recorrer à tabela de página para os encontrar.

## _Translation Lookaside Buffer_ ou TLB

![TLB](./assets/0005-TLB.png#dark=3)

Tal como em qualquer outra cache, a TLB pode ser organizada de modo a ser totalmente
associativa, ou diretamente mapeada. O tempo de acesso à TLB é tipicamente menor que
o tempo de acesso à cache visto que as TLBs são muito menores!
associativa, ou diretamente mapeada. O tempo de acesso à TLB é extremamente menor que
o tempo de acesso à cache visto que as TLBs são muito menores e são desenhadas para ser rápidas!

![TLB](./assets/0005-TLBmem.png#dark=3)

Se uma página é carregada para a memória principal, então o _miss_ na TLB pode ser
tratado, tanto em _hardware_ como em _software_ se carregarmos a [tradução](color:pink)
da informação da tabela de página para a TLB. Sabendo que demora dezenas de ciclos para
econtrar e carregar a tradução da informação para a TLB, quando a página não se
encontra na memória principal, demora milhares de ciclos, e temos, por isso, uma
_true page fault_. É importante referir que os _misses_ são bastante mais comuns que
as _true page faults_.

Assim, quando ocorre um [TLB _miss_](color:pink), é necessário reconhecer como _miss_
antes que o registo do destino seja sobrescrito, dando origem a uma exceção. Assim
o _handler_ copia o PTE da memória para a TLB, reiniciando a instrução e, caso a página
Expand All @@ -170,7 +150,7 @@ por isso, a instrução que falha é reiniciada.
![Interação](./assets/0005-interaction.png#dark=3)

Se a tag da cache usa um endereço de memória, é necessário traduzir antes de ir
procurar a cache. Uma alternatica é usar uma tag de endereço de memória virtual,
procurar a cache. Uma alternativa é usar uma tag de endereço de memória virtual,
contudo, tal pode ser complicado graças a _aliasing_, isto é, diferentes endereços
virtuais para endereços físicos partilhados.

Expand All @@ -185,12 +165,12 @@ memória torna-se inconsistente.

### Redução do Tempo de Tradução

Sabendo a interpretação de endereços virtuais pela TLB é representada da seguinte forma:
Sabendo que a interpretação de endereços virtuais pela TLB é representada da seguinte forma:

| Virtual page index | Virtual offset |
| :----------------: | :------------: |

E a interpretação dos endereços físicos pela cache é representada da seguinte forma:
E que a interpretação dos endereços físicos pela cache é representada da seguinte forma:

| Tag | Index | Offset |
| :-: | :---: | :----: |
Expand Down Expand Up @@ -221,11 +201,11 @@ Por isso, um dos seguintes cenários pode ocorrer:
## Proteção de Memória

Como é evidente, diferentes tarefas podem partilhar partes dos seus espaços de
endereçamento virtual, mas é necessário [proteger contrar acessos errantes](color:pink).
Para tal, precisamos de ajuda do OS.
endereçamento virtual, mas é necessário [proteger contra acessos errantes](color:pink).
Para tal, precisamos de ajuda do sistema operativo.

Visto que o suporte _hardware_ para porteção do OS, temos um modo supervisor
previlegiado, isto é, o [_kernel mode_](color:purple), instruções previlegiadas,
Existe suporte de _hardware_ para proteção do OS, pelo que temos um modo supervisor
privilegiado, isto é, o [_kernel mode_](color:purple), instruções privilegiadas,
tabelas de páginas e outros estados de informação que só podem ser acedidos com o modo
supervisor e uma chamada de exceção do sistema.

Expand All @@ -249,12 +229,10 @@ nível de hierarquia temos:
miss que pode ser através da política LRU ou aleatório; em termos de memória
virtual o LRU tem uma aproximação com o suporte de _hardware_.

- [_Write Policy_](color:pink): como já tinhamos visto anteriormente, podemos ter
_write-throughs_ que atualizam tanto os níveis superiores como inferiorese
simplificam a troca mas precisam de um _buffer_ de escrita; ou _write-backs_ que apenas atualizam os níveis superior e só atualizam os inferiores quando o bloco é
- [_Write Policy_](color:pink): como já tínhamos visto anteriormente, podemos ter
_write-throughs_ que atualizam tanto os níveis superiores como inferiores e
simplificam a troca mas precisam de um _buffer_ de escrita; ou _write-backs_
que apenas atualizam os níveis superior e só atualizam os inferiores quando o bloco é
resposto, ou seja, é necessário manter mais estado. Assim, em termos de memória
virtual, só o _write-back_ é fazível, visto que existe uma latência de escrita no
disco.

[_Cache Design Trade-offs_](color:green)
![_Cache Design Trade-offs_](./assets/0005-design.png#dark=3)
Binary file removed content/oc/assets/0005-design.png
Binary file not shown.

0 comments on commit ccf9156

Please sign in to comment.