5.2.4 Monitorando o Desempenho da Máquina
A simulação é útil não apenas para verificar a correção de um projeto de máquina proposto, mas também para medir o desempenho da máquina. Por exemplo, podemos instalar em nosso programa de simulação um "medidor" que mede o número de operações de pilha usadas em uma computação. Para fazer isso, modificamos nossa pilha simulada para manter o controle do número de vezes que registradores são salvos na pilha e a profundidade máxima alcançada pela pilha, e adicionamos uma mensagem à interface da pilha que imprime as estatísticas, como mostrado abaixo. Também adicionamos uma operação ao modelo de máquina básico para imprimir as estatísticas da pilha, inicializando the_ops em make_new_machine para:
list(list("initialize_stack",
() => stack("initialize")),
list("print_stack_statistics",
() => stack("print_statistics")));
Aqui está a nova versão de make_stack:
Os exercícios 5.13 a 5.18 descrevem outros recursos úteis de monitoramento e depuração que podem ser adicionados ao simulador de máquina de registradores.
Exercício 5.13
Meça o número de pushes e a profundidade máxima de pilha necessária para calcular n! para vários valores pequenos de n usando a máquina fatorial mostrada na Figura 5.11. A partir de seus dados, determine fórmulas em termos de n para o número total de operações push e a profundidade máxima de pilha usada no cálculo de n! para qualquer n > 1. Observe que cada uma dessas é uma função linear de n e é, portanto, determinada por duas constantes. Para obter as estatísticas impressas, você terá que aumentar a máquina fatorial com instruções para inicializar a pilha e imprimir as estatísticas. Você pode querer também modificar a máquina para que ela leia repetidamente um valor para n, calcule o fatorial e imprima o resultado (como fizemos para a máquina MDC na figura 5.4), para que você não tenha que invocar repetidamente get_register_contents, set_register_contents e start.
Exercício 5.14
Adicione contagem de instruções à simulação de máquina de registradores. Ou seja, faça o modelo de máquina manter o controle do número de instruções executadas. Estenda a interface do modelo de máquina para aceitar uma nova mensagem que imprima o valor da contagem de instruções e redefine a contagem para zero.
Exercício 5.15
Aumente o simulador para fornecer rastreamento de instruções. Ou seja, antes de cada instrução ser executada, o simulador deve imprimir a instrução. Faça o modelo de máquina aceitar mensagens trace_on e trace_off para ativar e desativar o rastreamento.
Exercício 5.16
Estenda o rastreamento de instruções do exercício 5.15 de modo que antes de imprimir uma instrução, o simulador imprima quaisquer rótulos que precedem imediatamente essa instrução na sequência do controlador. Tenha cuidado para fazer isso de uma maneira que não interfira com a contagem de instruções (exercício 5.14). Você terá que fazer o simulador reter a informação de rótulo necessária.
Exercício 5.17
Modifique a função make_register da seção 5.2.1 de modo que os registradores possam ser rastreados. Registradores devem aceitar mensagens que ativam e desativam o rastreamento. Quando um registrador é rastreado, atribuir um valor ao registrador deve imprimir o nome do registrador, o conteúdo antigo do registrador e o novo conteúdo sendo atribuído. Estenda a interface ao modelo de máquina para permitir que você ative e desative o rastreamento para registradores de máquina designados.
Exercício 5.18
Alyssa P. Hacker quer um recurso de breakpoint no simulador para ajudá-la a depurar seus projetos de máquina. Você foi contratado para instalar este recurso para ela. Ela quer poder especificar um lugar na sequência do controlador onde o simulador irá parar e permitir que ela examine o estado da máquina. Você deve implementar uma função:
set_breakpoint(machine, label, n)
que define um breakpoint logo antes da n-ésima instrução após o rótulo dado. Por exemplo,
set_breakpoint(gcd_machine, "test_b", 4)
instala um breakpoint em gcd_machine logo antes da atribuição ao registrador a. Quando o simulador alcança o breakpoint, ele deve imprimir o rótulo e o deslocamento do breakpoint e parar de executar instruções. Alyssa pode então usar get_register_contents e set_register_contents para manipular o estado da máquina simulada. Ela deve então poder continuar a execução dizendo:
proceed_machine(machine)
Ela deve também poder remover um breakpoint específico por meio de:
cancel_breakpoint(machine, label, n)
ou remover todos os breakpoints por meio de:
cancel_all_breakpoints(machine)