Arquitectura
de Computadores
Curso: LMCC
Exame 1ª Chamada 99/02/01
Duração: 2h
Componente teórica ; Componente
prática
Componente Teórica
1. Considere a instrução HLL que efectua uma
subtracção de dois valores do tipo inteiro, dentro de um
procedimento folha - i.e., que não invoca outro - para
execução num computador com CPU MIPS.
a) Considere que os 3
valores referidos nessa instrução (um de destino, 2 de
fonte) são variáveis escalares do tipo inteiro, sendo uma
das variáveis fonte o 2º parâmetro recebido pelo
procedimento, e as restantes variáveis locais ao
procedimento. Calcule (e apresente os cálculos) o
espaço que esta mesma instrução ocupa em memória (medido
em células de memória do MIPS), considerando que ela está:
(i) em HLL; (ii) em assembly; (iii) em linguagem
máquina.
Resolução
b) Comente as seguintes
afirmações:
(i) "Para executar um
programa escrito em HLL basta usar um compilador com assembler
(para transformar um ficheiro de texto num executável),
que ele fica logo em memória pronto a ser
executado."
Resolução
(ii) "A arquitectura
do MIPS é mais poderosa que a dos processadores da Intel
porque suporta um instruction set enriquecido com
muitas pseudo-instruções".
Resolução
c) Considere agora que
o outro valor especificado como fonte é uma constante.
Caracterize a arquitectura do MIPS (e respectivo instruction
set) relativamente à possível localização das
varáveis especificadas nesta instrução e à sua dimensão.
Resolução
d) Na continuação da
alínea anterior, considere agora que a constante tem o valor
-18 e que a variável destino não é escalar, mas sim
o 3º campo de um record local, cujo apontador está
num registo "seguro", e sendo o record
declarado no procedimento após a declaração de 3
variáveis do tipo inteiro (o 1º campo do record é
constituído por uma string de 8 caracteres, e o 2º
campo um real de precisão simples).
Utilizando a convenção de
utilização de registos, escreva o código que executa essa
instrução HLL em assembly do MIPS (sem
pseudo-instruções).
Resolução
e) Considerando
os mesmos pressupostos que a alínea anterior e, utilizando a
sintaxe do MIPS, rescreva o código que executa essa
instrução HLL em assembly duma outra arquitectura
com poucos registos (em que as variáveis estão normalmente
armazenadas em memória) e em que só é possível
especificar 2 operandos numa operação aritmética (estando
um deles obrigatoriamente num registo genérico, e podendo o
outro estar em memória).
Os modos de endereçamento
permitidos são (R + R) e (R + deslocamento_16_bits). O
endereço das variáveis fonte e destino em memória é,
respectivamente, ($fp) + 8 , e Mem [($fp) - 16] +
10.
Resolução
2. Vários engenheiros
foram consultados para apresentarem sugestões para a melhoria do
desempenho dum dado computador. Sabe-se que o tempo de CPU
necessário para a execução de um dado programa pode ser
relacionado com vários factores, de acordo com a seguinte
equação:
CPUtime
= N.º Instr * CPI * período do clock
Comente as seguintes afirmações
desses engenheiros:
a) "Vamos
enriquecer o instruction set, aproximando-o duma HLL;
assim consegue-se diminuir o n.º de instruções gerado pelo
compilador, melhorando sempre o desempenho do
computador".
Resolução
b) "Vamos
diminuir a latência da sua cache no acesso à
memória principal (reduz o CPI), aumentando o n.º de bancos
de memória ligados aos barramentos e colocando-os em
paralelo".
Resolução
Resolução da
Componente Teórica
Alberto José Proença
1. a) Resolução
Aparentemente este exercício não
parece oferecer qq dificuldade! Será que é mesmo verdade? De
acordo com o enunciado, estas 5 alíneas referem-se todas a uma
simples subtracção de 2 inteiros? Tanto tempo gasto nas aulas
práticas com exercícios complexos, e agora no exame sai uma
pergunta tão simples?
Deveria ser este o vosso espírito
quando confrontados com esta questão. E com optimismo deveriam
continuar...
O enunciado da 1ª alínea dá-nos
alguma informação adicional, nomeadamente quanto á
localização das variáveis nele presentes: "sendo uma das
variáveis fonte o 2º parâmetro recebido pelo
procedimento" e quanto à outras variáveis, elas são
"variáveis locais ao procedimento".
Ora, no caso do MIPS, isto sugere-nos logo que os 3 operandos a
especificar na operação de subtracção deverão estar, em
princípio, em registos.
E se já estão em registos, então deve ser possível
implementar essa operação de subtracção usando apenas uma
instrução em linguagem máquina do MIPS, com formato R.
Óptimo!
Que nos pede esta alínea? Que
calculemos o espaço que a instrução ocupa em memória...
Fácil!
(i) "em
HLL": se ela estiver redigida da maneira "a = b -
c", então ela é constituída por 5 caracteres visíveis
("a", "=", "b", "-", e
"c") e 4 espaços entre eles (puro texto, não se
esqueçam!), codificados portanto por 9 caracteres ASCII; se cada
um tem a dimensão de 8 bits, e se cada célula de memória do
MIPS tb tem a dimensão de 8 bits, então cada caracter ocupa uma
célula de memória; logo, a resposta de (i) é 9
células de memória.
(ii) "em assembly":
esta operação em assembly, de acordo com os
comentários feitos anteriormente, pode ser implementada usando
apenas uma instrução, do tipo "sub $t0, $a0, $t1";
mas... (atenção que é aqui que os vossos colegas falham quase
sempre!) uma instrução em assembly continua a ser puro
texto! Ou seja, esta instrução é constituída por 14
caracteres visíveis (se não me enganei a contá-los) mais 3
espaços, num total de 17 caracteres que serão codificados para
ASCII, ocupando assim 17 células de memória.
(iii) "em
linguagem máquina": uma instrução em assembly (e
que não é pseudo-instrução!) corresponde a uma e só uma
instrução em linguagem máquina; no caso do MIPS (e da maioria
das arquitecuras RISC) todas as instruções em linguagem
máquina tem sempre a mesma dimensão, que é de 32 bits (no caso
do MIPS a sua codificação obedece a 3 formatos, R, I e J); ora
32 bits ocupam 4 células de memória.
Regressar
ao enunciado
1. b) (i) Resolução
O enunciado pede para comentar as
afirmações de 2 sub-alíneas. Cada uma delas conterá,
provavelmente mais que uma afirmação, pelo que o primeiro passo
será identificar quais são as afirmaçãoes que se fazem, para
depois as comentar.
Neste caso, 2 afirmações são
facilmente identificadas (já as viu?): "basta usar" e
"fica logo". Vamos a elas:
- "basta usar um
compilador com assembler (para transformar um
ficheiro de texto num executável)": parece correcta
a afirmação; com efeito, se usasse apenas um
compilador, este iria traduzir um programa escrito num
nível alto de abstracção do computador (o programa
fonte, obviamente um ficheiro de texto), para outro num
nível mais baixo; assim, usando o compilador mais o assembler,
sei que para além de o ter traduzido para um nível mais
baixo, o assembler tb "montou" o
programa (traduzido) em linguagem máquina; mas será que
este programa estará pronto a ser executado, i.e., que o
ficheiro resultante é um executável como se afirma no
enunciado? Pouco provável, pois irá quase de certeza
necessitar de funções/sub-rotinas duma biblioteca de
programas e/ou de se juntar a outros módulos compilados
separadamente, pelo que sem passar pelo linker,
o produto do compilador+assembler não será
suficiente para produzir um ficheiro executável, embora
produza um ficheiro objecto;
- "fica logo em memória
pronto a ser executado": mesmo que tivesse passado
pelo linker, o resultado do compilador+assembler
é sempre um ficheiro (armazenado em disco), para mais
tarde ser carregado na memória pelo loader, o
qual tem ainda outras funções (relembre a matéria em http://gec.di.uminho.pt/discip/TextoAC/cap3.html#UtilMem )
Regressar
ao enunciado
1. b) (ii) Resolução
Neste segundo caso, apenas 1
afirmação é feita, "é mais poderosa ... porque...":
- qual o factor que é
apresentado para justificar a afirmação de que a
"arquitectura do MIPS é mais poderosa que a dos
processadores da Intel"? Se este factor for
verdadeiro, então teremos que analisar se a afirmação
resultante desse factor é ou não verdadeira; se o
factor for falso, então a afirmação cai pela raiz.
Qual é então o factor? Diz o enunciado que
"suporta um instruction set enriquecido com
muitas pseudo-instruções". O que é que tem a ver
uma coisa com outra? Se lhe disserem que um Ferrari é
mais potente que um Porsche porque o banco do condutor é
regulável em altura, que respondia a essa pessoa? Aqui o
pb é parecido: as pseudo-instruções são apenas uma
forma de os assemblers facilitarem a vida aos
programadores em assembly, disponibilizando-lhes
instruções fictícias que o instruction set do
processador não suporta (por questões de eficiência),
mas que dariam jeito ao programador que lá estivessem;
compete depois ao assembler converter essas
pseudo-instruções na(s) instrução(ões) efectiva(s)
que cada processador suporta.
Regressar
ao enunciado
1. c) Resolução
De acordo agora com esta alínea,
os operandos, do tipo inteiro (escalar, não-estruturado), são
então: um (fonte) é um parâmetro, o outro (fonte) é uma
constante, e o outro (destino) é uma variável local.
Pretende-se que se caracterize a arquitectura (e respectivo instruction
set) relativamente a "localização das varáveis
especificadas nesta instrução" e "sua
dimensão".
Por outras palavras: onde é que o
MIPS pode colocar estes operandos, e qual a sua dimensão.
Comecemos pela dimensão: estamos
a falar de inteiros; vimos nas aulas teóricas e práticas como
é que estes valores podem ser representados (não se esqueçam
que a classe dos inteiros inclui os valores negativos e os
positivos), com especial destaque para a representação dos
valores negativos (em complemento para 2 em todas as
arquitecturas actuais), e chamando a atenção para o facto de
que, embora a utilização de um nº pequeno de bits para
representar os inteiros poderia trazer economia nos requesitos de
memória, essa solução poderia ser inaceitável para a maioria
das aplicações (por ex., com 16 bits não consigo representar o
nº 50 000!).
Assim, todas as arquitecturas RISC actuais trabalham com registos
(onde se encontram as variáveis na altura das operações
aritméticas/lógicas) e unidades aritméticas de 32 ou 64 bits,
para permitirem a representação e operação (rápida) de
inteiros com 32 ou 64 bits.
E o caso das constantes que vem especificadas na própria
instrução? Com um formato de instrução de 32 bits, não é
possível incluir lá tb um valor numérico de 32 bits. No caso
do MIPS, este apenas suporta um formato de instrução em que é
possível especificar uma constante (formato I), e essa constante
não pode ter mais 16 bits, i.e., está em complemento para 2, e
pertence ao intervalo [-32k , +32k[ .
Quanto à sua colocação: na
altura da execução da operação aritmética, os operandos têm
de estar em registo (regra comum a todas as arquitecturas RISC):
se forem variáveis, estarão num dos 32 registos genéricos do
processador; se for uma constante, estará no registo que contém
a instrução, uma vez que o valor da constante vem especificado
no próprio formato da instrução.
Se os operandos não estiverem em registos - em princípio as
variáveis escalares são alocadas a registos, mas se não houver
registos que cheguem, as variáveis terão de ser alocadas em
posições de memória - então é preciso transferi-las primeiro
da memória para os registos.
A arquitectura do MIPS ao nível do seu instruction set
possui instruções para este tipo de tranasferência de
informação, mas o MIPS apenas dispõe de um modo de especificar
um endereço de memória: fornecendo no formato da instrução a
referência a um registo, e um valor numérico de 16-bits; o
endereço é calculado adicionando o conteúdo desse registo ao
valor numérico de 16-bits (com um valor em complemento para 2).
Regressar
ao enunciado
1. d) Resolução
Relembrando o enunciado, o que se
estava a analisar era a operação de subtracção de inteiros
" a = b - c ".
Já se sabe agora que "a" é "o 3º campo de um record
local, cujo apontador está num registo seguro", que
"b" (ou "c") é um parâmetro recebido pelo
procedimento, e que "c" (ou "b") é a
constante com o valor -18.
Analisando melhor o enunciado, há os seguintes aspectos a
comentar:
- a operação de subtracção
está "dentro de um procedimento"; que
informação está aqui que seja pertinente? Bom, que as
variáveis que são locais ao procedimento deverão ser
alocadas a registos do tipo $s ou do tipo $t, dependendo
se o ... (pensem vocês!); se forem parâmetros poderão
estar em registos do tipo $a;
- o procedimento é
"folha"; ah! isto já nos vai ajudar a resolver
a questão colocada acima, porque se este procedimento
não chama outro, então não precisa de se preocupar em
utilizar os registos seguros ($s) para as variáveis
locais, pois os registos temporários ($t) são
igualmente seguros!
- um valor fonte é "o 2º
parâmetro recebido pelo procedimento": o enunciado
não diz se este valor fonte é o "b" ou o
"c"... e aparentemente pode ser qq um dos dois.
Logo, posso escolher! Vou escolher ser o "b"; e
se é o 2º parâmetro, então deverá estar em $a1 (e
até cabe lá porque é um inteiro, logo um valor de 32
bits representado em complemento para 2);
- o outro valor especificado
como fonte é uma constante que "tem o valor -18":
então o 2º operando, que designei por "c",
não é uma variável! É uma constante, e as constantes
não são normalmente alocadas a registos, mas sim
incluídas no próprio código da instrução (quando
este suporta); neste caso o valor é pequeno (-18) e cabe
perfeitamente no próprio código da instrução com a
operação que se pretende fazer, senão... (que se faria
se não coubesse?Pensem!); mas... outro problema: se
olharmos para o instruction set do MIPS
(distribuído com o enunciado do exame), verificamos que
ele não tem operação de subtracção com formato de
instrução "I"; e o enunciado é claro quando
diz "sem pseudo-instruções"! Que fazer?...
(isto é canja!... nem é preciso dizer.)
- a variável destino é
"o 3º campo de um record local": ah!,
finalmente uma variável local! Representa "a"
na expressão que escrevi em cima; mas... é um campo de
um record. E um record é uma variável
estruturada, e como tal os compiladores não costumam
alocar variáveis estruturadas a registos; colocam-nas
antes em posições de memória, e quando muito alocam
registos (que conviriam que fossem seguros) para ficarem
com os apontadores para a posição de memória onde
começam as variáveis estruturadas; que diz mais o
enunciado? Diz "cujo apontador está num registo
seguro", ah, tal como prevíamos; e em qual? Diz o
enunciado "sendo o record declarado no
procedimento após a declaração de 3 variáveis do tipo
inteiro"; então, o apontador para o record
deverá estar no 4º registo seguro, i.e., em $s3 (ou
será que me enganei? Que acham?);
- se o apontador para o record
está num registo seguro, onde é que efectivamente se
encontra localizada a variável destino? O enunciado diz
que é o "3º campo", logo é preciso saber
qual a dimensão dos dois primeiros campos, para calcular
a distância entre o início do record e o
início do 3º campo, para se poder especificar
completamente o endereço de memória onde deverá ser
armazenado o resultado da subtracção; mas nisto o
enunciado é claro, quando diz "o 1º campo do record
é constituído por uma string de 8 caracteres, e
o 2º campo um real de precisão simples"; de caras:
o 1º campo ocupa 8 bytes (= 8 células de memória) e o
2º campo, de acordo com a norma IEEE, ocupa 32 bits,
i.e., 4 bytes ou 4 células; logo, o 3º campo
encontra-se à distância de (8+4=) 12 células do
início do record;
- bom, agora que sabemos onde
estão todos os operandos e qual a operação que devemos
usar retirada do instruction set do MIPS (estou
a partir do princípio que não tiveram dificuldade em
chegar à conclusão que terão de fazer a adição
" a = b + ( -c) " ), há que relembrar que em
todas as arquitecturas RISC não é permitido efectuar
operações aritméticas/lógicas com operandos em
memória; neste caso, o operando destino "a"
está em memória, pelo que é preciso guardar
provisoriamente o resultado da operação num registo (é
para isso que servem tb os registos $v), e de seguida
efectuar uma transferência desse valor para a posição
de memória correcta, i.e., uma operação de store
para a posição de memória cujo endereço é dado pela
soma do conteúdo do registo que contém o apontador do record
(que já verificaram ser o $t3) com a distância a que o
3º campo se encontra do início do record (+12).
E pronto: parece que já temos
todos os dados para resolver o pb, e todas as sugestões para lá
chegarem sem dificuldade! Queriam mais simples? Uma resposta com
apenas 2 instruções, um addi seguido de um sw?
Regressar
ao enunciado
1. e) Resolução
Uff! Não é preciso estudar outra
vez cuidadosamente o enunciado, uma vez que se diz
"Considerando os mesmos pressupostos"; e além disso,
posso tb usar uma sintaxe familiar, que é a do MIPS. Por outro
lado, tenho que ver em que é que esta "outra
arquitectura" difere da do MIPS, para ver se consigo
perceber onde tenho de mudar o raciocínio, relativamente à
execução da alínea anterior.
Quatro grandes diferenças são
apontadas no enunciado (já as detectou? Pense um bocado antes de
avançar, pois no dia do exame não vai ter esta ajuda...). São
elas:
- "as variáveis estão
normalmente armazenadas em memória"; não tenho
registos que cheguem para alocar variáveis locais nem
para passar parâmetros; isto faz-me lembrar a
arquitectura do i86, conforme analisado na aula e
documentado em http://gec.di.uminho.pt/discip/TextoAC/cap6.html#i86; com uma diferença: o enunciado
não diz que esta arquitectura apenas trabalha com
inteiros de 16-bits (caso do i86) e que quando invoca uma
sub-rotina guarda na stack o valor do PC (com o
endereço de retorno da sub-rotina); se não diz, vou
considerar que, relativamente a estes 2 aspectos, esta
arquitectura é semelhante ao MIPS, i.e., inteiros são
de 32-bits e existe um registo especial para o endereço
de retorno;
- "só é possível
especificar 2 operandos numa operação
aritmética"; no MIPS posso sempre especificar 3;
neste caso, e se vou usar a sintaxe do MIPS, isto vai
obrigar a que, sempre que uma operação
aritmética/lógica (tipo R) especifique 3 operandos no
formato da instrução, 2 deles têm de ser iguais (um
fonte e um destino);
- "podendo o outro estar
em memória"; no MIPS nenhum operando pode estar em
memória; aqui posso ter 1 operando em memória, e não
mais que 1, pois o enunciado diz claramente "estando
um deles obrigatoriamente num registo genérico";
como ele tem poucos registos, vou considerar que todos os
que tem serão usados apenas para valores temporários, e
vou chamá-los de $t;
- "Os modos de
endereçamento permitidos são (R + R) e (R +
deslocamento_16_bits)"; o MIPS apenas suporta o 2º
modo de endereçamento aqui especificado; em termos de
sintaxe, se precisar de usar o 1º modo representá-lo-ei
na forma $x($y).
Quais são então as implicações
destas diferenças?
A implicação maior é certamente
a resultante do facto de as variáveis estarem agora todas em
memória; conforme estudado no cap.6 (referido acima), as
arquitecturas que não tenham registos suficientes para alocar as
variáveis locias e os parâmetros, utilizam uma estrutura de
dados na stack - para conter a informação pertinente
às variáveis necessárias na execução de qq função ou
procedimento - que é designada de activation record,
normalmente constituída pelos seguintes campos (informação
retirada das notas de estudo):
- campo para os parâmetros a
passar, sejam eles valores de variáveis ou apontadores
para variáveis em memória;
- campo para salvaguarda dos
registos pelo procedimento/função "chamador";
- campo para salvaguarda do
endereço de retorno (colocado na stack pela
execução da instrução de invocação de subrotina);
- campo para salvaguarda dos
registos pelo procedimento/função "chamado";
- campo para salvaguarda do
conteúdo do registo que apontava para o activation
record do procedimento/função "chamador"
(registo BP no i86, com função equivalente ao registo
$fp no MIPS);
- campo reservado para as
variáveis locais do procedimento/função.
Se quiséssemos saber a
localização de cada uma das variáveis, é possível
calculá-las com alguns pressupostos. Neste exame tal não é
necessário, uma vez que o enunciado diz que "o endereço
das variáveis fonte e destino em memória é,
respectivamente...". Se o seu endereço não fosse indicado,
tb se poderia lá chegar, através da seguinte análise de cada
campo do activation record:
- o 1º campo terá pelo menos
2 parâmetros, uma vez que o enunciado diz que "um
valor fonte é o 2º parâmetro recebido pelo
procedimento", e sendo o 2º parâmetro o que
estiver mais próximo do topo da stack; vamos
assumir que não haverá mais parâmetros;
- dada a ausência de
informação suficiente no enunciado para saber o que
deverá estar no 2º campo (que nada nos diz sobre o
procedimento/função "chamador"), vamos
considerá-lo vazio;
- o 3º campo já foi
comentado; logo, assumimos vazio;
- o 4º campo seria para
armazenar o conteúdo dos registos $s usados pelo
procedimento e do $ra; na ausência de informação
suficiente no enunciado, vou igualmente considerar vazio
este campo;
- o 5º campo irá conter o
valor que estava no $fp, e o novo valor do $fp ficará a
apontar para a célula de memória que contém o início
deste campo (sendo um inteiro, é um campo com 4 bytes,
i.e., com 4 células);
- o 6º campo (das variáveis
locais) foi assim construído na stack: primeiro
reservou-se espaço para as 3 primeiras variáveis, do
tipo inteiro, e depois para o record, que apenas
sabemos que tem pelo menos 3 campos e respectiva
dimensão (vem tudo no enunciado antes da alínea a).
Com base nesta informação (a
estrutura do activation record) seria possível então
saber a localização do operando fonte - 2º parâmetro, em +4($fp)
- e do operando destino - 3º campo do record; o início do
record está em -28($fp), estando o 3º campo 12 posições
depois, em -16($fp).
Contudo, neste exame não se pede
tanto, uma vez que se fornecem valores (fictícios) para a sua
localização!
Embora a localização do operando destino tb não seja imediata:
em -16($fp) está o apontador para uma posição
de memória; ao conteúdo dessa posição de memória (que
deverá ser um inteiro) ter-se-á de adicionar o valor +10
para se ter o apontador para o operando destino!
Quanto ao operando fonte, não parece existir dificuldade: ele
encontra-se em +8($fp).
A 2ª maior implicação das
diferenças identificadas acima diz respeito ao número de
operandos permitido em cada operação. Isto significa que nossa
operação " a = b - c ", que já tinha sido alterada
para " a = b + (-c) " e para " a = b + 18 ",
vai ter de ter uma implementação do tipo:
temp = b (uma operação
de load para um registo que poderemos chamar de
$t0)
temp = temp + 18 ( uma operação de addi)
a = temp (uma operação de store)
Atenção a quem se sentir tentado
a poupar aqui uma instrução, substituindo as 2 primeiras por
" b = b + 18 ", pois isso significa que vai alterar o
valor da variável "b" sem estar na especificação do
problema!
Resta-nos ver agora se as outras
diferenças e se as localizações dos operandos permitem que a
implementação final fique com apenas estas 3 instruções.
A primeira instrução é
pacífica, pois estando localizada em em +8($fp)
a instrução lw executa o pretendido, para $t0.
Idêntico comentário se aplica à 2ª instrução: é apenas um addi.
A terceira é mais complicada, tanto mais que o modo de
endereçamento adicional disponibilizado por esta arquitectura
não nos vai servir de nada. Esta operação ( a = temp ) tem de
ser implementada por mais que uma instrução: primeiro é
preciso ler da memória (para um outro registo temporário, por
ex. $t1) o conteúdo da célula que tem o endereço -16($fp),
e só depois se pode efectuar a operação de store (sw)
do conteúdo de $t0 para a posição +10($t1).
Comentário final: esta alínea
não era fácil; tinha como objectivo identificar os estudantes
que dominavam melhor a matéria, i.e., que sabiam aplicar os
conhecimentos adquiridos, para além de um simples despejar da
informação memorizada na véspera.
Regressar
ao enunciado
2. a) Resolução
Vou repetir a afirmação do
engenheiro que devemos comentar:
"Vamos enriquecer o instruction set, aproximando-o
duma HLL; assim consegue-se diminuir o n.º de instruções
gerado pelo compilador, melhorando sempre o desempenho do
computador."
Vamos analisar esta frase, para
identificarmos quais as afirmações que ela contém e que
precisam de ser comentadas. Tentem lá chegar antes de continuar
a ler o texto.
Eis as afirmações nela
presentes:
- "Vamos enriquecer o
instruction set... assim consegue-se diminuir..."
Será verdade o que ele diz? Faz sentido?
- "... assim consegue-se
diminuir ... melhorando sempre o desempenho..."
Melhora? E se melhora, melhora sempre?
Vamos aos comentários.
- Efectivamente, uma das formas
de enriquecer o instruction set é aproximando-o
duma HLL; agora se este enriquecimento vai "diminuir
o n.º de instruções gerado pelo compilador" é o
que precisamos de ver. Em princípio, quanto mais
próximo estiver uma linguagem máquina duma HLL, menor
será o nº de instruções geradas pelo compilador para
executar as tarefas pretendidas com o código que se
escreveu. Portanto, esta afirmação parece correcta e
faz sentido.
- Mas será que melhora o
desempenho? De acordo com a fórmula apresentada, que
relaciona o tempo de execução de uma aplicação no CPU
com diversos factores (3, mais concretamente), se se
melhorar um desses factores (neste caso, o nº de
instruções executadas) sem alterar os outros, não há
dúvida que temos uma melhoria do desempenho. Mas se se
enriquecer o instruction set, não vai afectar os
outros factores?
Um instruction set mais rico implica normalmente a
existência de instruções mais complexas, e como tal,
mais demoradas a executar; isto vai afectar
(negativamente) o tempo médio de execução de cada
instrução, incluído num outro factor da fórmula
referida (o CPI). Se o que se ganhar com a redução do
nº de instruções executadas não compensar o que se
perder com o aumento do CPI, então pode não melhorar.
Por outro lado, um instruction set mais rico pode
implicar, não apenas a existência de instruções mais
complexas, mas tb de um maior nº de instruções; o hardware
para a descodificação da instrução poderá ser mais
complexo, podendo ter como consequência uma maior
dificuldade ao nível da microelectrónica em se manter
uma frequência de clock tão elevada, i.e.,
poderá impôr um aumento do período do clock
(que é outro factor na fórmula acima referida).
Ou seja, muito provavelmente o enriquecimento do instruction
set não irá melhorar o desempenho; e de certeza que
afirmar "melhorando sempre o desempenho" não
faz sentido.
Regressar
ao enunciado
2. b) Resolução
Vou repetir a afirmação do
engenheiro que devemos comentar:
"Vamos diminuir a latência da sua cache no acesso à
memória principal (reduz o CPI), aumentando o n.º de bancos de
memória ligados aos barramentos e colocando-os em
paralelo".
Vamos analisar esta frase, para
identificarmos quais as afirmações que ela contém e que
precisam de ser comentadas. Tentem lá chegar antes de continuar
a ler o texto.
Eis as afirmações nela
presentes:
- "Vamos diminuir a
latência da sua cache no acesso à memória...
reduz o CPI..." Será verdade o que ele diz? Faz
sentido?
- "Vamos diminuir a
latência da sua cache no acesso à memória...
aumentando o n.º de bancos de memória...". É
assim que se diminui a latência?
- "Vamos diminuir a
latência da sua cache no acesso à memória...
colocando-os (aos
bancos de memória) em paralelo". É assim que se diminui
a latência?
Vamos aos comentários.
- O CPI é uma medida do tempo
médio de execução de uma instrução (a unidade não
é o segundo, mas sim o período de clock), o
qual depende não apenas do tempo que a instrução leva
ser executada no CPU, mas tb do tempo adicional que ela
possa precisar para aceder à memória, quando para tal
não é suficiente o que se previu para a sua execução
no CPU.
Esse tempo adicional depende essencialmente de 2
factores: (i) o nº de vezes que, em média, cada
instrução tem de aceder à memória (que sabemos ser
sempre superior a 1, pois para além de ser necessário
ir buscar a própria instrução à memória, uma dada
percentagem das instruções de um programa ainda tem de
ir à memória durante a sua execução, como é o caso
das instruções de load/store), e (ii)
a penalização que ocorre por cada acesso.
Esta penalização tem tb pelo menos 2 parcelas: (i) a
penalização por aceder ao nível da hierarquia de
memória em causa (neste caso, o acesso à própria
cache), e (ii) a penalização por aceder ao nível
inferior da hierarquia de memória (normalmente mais
pesada que a anterior); este problema debruça-se sobre
esta segunda parcela, tal como está escrito: "latência
da sua cache no acesso à memória principal".
Assim, se o CPI depende do tempo adicional para aceder à
memória, se este tempo depende da penalização que
ocorre por cada aceso, e se essa penalização contém a
parcela referente ao acesso ao nível inferior da
hierarquia de memória, é óbvio que se "diminuir a
latência da sua cache no acesso à memória"
se "reduz o CPI".
- A questão que se coloca
agora é como diminuir então essa latência no acesso à
memória principal? Para se melhorar o desempenho no
acesso à memória, há 2 características a considerar:
o tempo para se ir buscar um bloco de informação, e o
nº de bytes de informação que podem ser transferidos
por unidade de tempo. À 1º característica é que se
designa de latência, enquanto a 2ª é mais conhecida
por largura de banda. A essência das afirmações
contidas nesta alínea têm a ver apenas com a latência.
O aumento do nº de bancos de memória sem mais nada,
não tem qq efeito. Por exemplo, o simples aumento da
capacidade de cozinha das cantinas (nº de almoços
confeccionados) não tem qq influência na redução do
tempo que cada um leva a encher o seu tabuleiro, i.e., a
latência de ser servido (assumindo que nunca se espera
que a refeição seja confeccionada).
Agora, se para além de se aumentar o nº de bancos de
memória se faz algo mais... então vamos ver a
afirmação seguinte.
- Aumentando "o n.º de
bancos de memória ligados aos barramentos e colocando-os
em paralelo" já parece fazer mais sentido. Poderia
corresponder na nossa metáfora, a dizer que se criava
mais um outro balcão para as pessoas se servirem. Mas
isto vai reduzir a latência de ser servido? Ou vai
apenas melhorar o nº de pessoas que são servidas por
unidade de tempo (correspondente à largura de banda)?
Mas tb poderia corresponder a uma disposição diferente
dos diversos elementos a servir no mesmo balcão, de modo
a que cada um se pudesse servir simultaneamente dos
talheres, da sopa, do sumo, ...
Por outras palavras, colocar mais bancos de memória em
paralelo vai reduzir o tempo de acesso a cada bloco de
informação? Bem, depende da dimensão do bloco... e da
maneira como são colocados em paralelo: se no mesmo
barramento, ou se se aumenta a largura do barramento.
Se a dimensão inicial do barramento era já a do bloco
de informação que é necessário transferir em cada
transacção (neste caso, uma linha da cache),
então a latência no acesso não ganha nada em se
aumentar o nº de bancos e na sua colocação em
paralelo.
Se a dimensão inicial do barramento era inferior à da
linha da cache (por ex, um barramento de dados
de 32 bits e uma cache com linhas de 2 ou 4
palavras cada), então o colocar mais bancos de memória
em paralelo pode melhorar se... (veja lá se descobre o
que deve vir a seguir a este se...) o acesso a esses
bancos não for feito sequencialmente, mas com alguma
forma de paralelismo, i.e., com pipeline (interleaving)
ou com aumento da largura do barramento de dados para a
dimensão da linha da cache.
Um comentário final: não se
assustem com esta proposta de resolução tão comprida, pois
não é necessário escreverem tudo isto para terem 100%!
Simplesmente entendi que os alunos mais fracos precisavam de
tanto detalhe para uma melhor compreensão da matéria, enquanto
que os melhores alunos gostariam que eu fosse até às últimas
consequências na análise destes casos. Na correcção dos
exames não sou tão exigente...
Regressar
ao enunciado