Von Neumann e a Máquina Moderna
Vídeo da aula estará disponível em breve
O modelo stored-program
Antes de 1945, computadores eram máquinas de propósito fixo. O ENIAC, por exemplo, era "programado" reconectando cabos e ajustando chaves — um processo que podia levar dias. Cada programa novo exigia reconfiguração física do hardware.
A ideia revolucionária de John von Neumann — publicada no relatório "First Draft of a Report on the EDVAC" (1945) — foi simples mas transformadora: armazenar o programa na mesma memória que os dados. O programa deixa de ser fiação física e passa a ser uma sequência de números na memória, indistinguível dos dados. A máquina lê instruções da memória, decodifica-as, e executa-as — uma de cada vez, sequencialmente.
Essa ideia — o stored-program concept — é o fundamento de praticamente todos os computadores que existem hoje. Do microcontrolador no seu forno de micro-ondas ao supercomputador que treina modelos de IA, todos seguem esse princípio.
Os cinco componentes
A arquitetura Von Neumann organiza o computador em cinco componentes fundamentais, conectados por barramentos:
┌─────────────────────────────────┐
│ CPU │
│ ┌───────────┐ ┌─────────────┐ │
│ │ UC │ │ ULA │ │
│ │ (Unidade │ │ (Unidade │ │
│ │ de │←→│ Lógica e │ │
│ │ Controle)│ │ Aritmética) │ │
│ └───────────┘ └─────────────┘ │
│ ↕ ↕ │
│ ┌────────────────────────────┐ │
│ │ Registradores (PC, IR, │ │
│ │ MAR, MBR, ACC, ...) │ │
│ └────────────────────────────┘ │
└──────────────┬────────────────────┘
│ Barramento
┌──────────────┴────────────────────┐
│ MEMÓRIA PRINCIPAL │
│ (armazena dados E instruções) │
└──────────────┬────────────────────┘
│ Barramento
┌──────────┐ ┌───────────┐
│ ENTRADA │ │ SAÍDA │
│ (teclado,│ │ (monitor, │
│ mouse) │ │ disco) │
└──────────┘ └───────────┘
Vamos detalhar cada componente:
1. Unidade Lógica e Aritmética (ULA / ALU) — realiza operações aritméticas (adição, subtração, multiplicação) e lógicas (AND, OR, NOT, comparações). A ULA recebe operandos dos registradores, executa a operação, e deposita o resultado de volta em um registrador. Ela também gera flags de status: zero (resultado é 0), carry (houve transbordo), negative (resultado negativo), overflow (estouro aritmético).
2. Unidade de Controle (UC) — o "maestro" do processador. A UC busca a próxima instrução na memória, decodifica-a para entender qual operação deve ser feita, e gera os sinais de controle que coordenam a ULA, os registradores, a memória e os dispositivos de E/S. Ela implementa o ciclo fetch-decode-execute.
3. Memória principal — armazena tanto dados quanto instruções (essa é a essência do stored-program). Cada posição de memória tem um endereço numérico único. A memória é volátil (RAM) — quando a energia é desligada, o conteúdo se perde.
4. Dispositivos de entrada — permitem que dados externos entrem no sistema: teclado, mouse, sensor, interface de rede, disco (quando lido).
5. Dispositivos de saída — permitem que resultados saiam do sistema: monitor, impressora, interface de rede, disco (quando escrito), alto-falante.
Registradores essenciais
Dentro da CPU, existem registradores com funções específicas no ciclo de execução. Eles são a memória mais rápida do computador — acessíveis em um único ciclo de clock:
Registrador Sigla Função
───────────── ─────── ─────────────────────────────────────────
Program PC Contém o endereço da PRÓXIMA instrução
Counter a ser buscada na memória
Instruction IR Contém a instrução ATUAL que está sendo
Register decodificada/executada
Memory MAR Contém o endereço de memória que será
Address Reg. acessado (leitura ou escrita)
Memory MBR Contém o dado que foi lido da memória
Buffer Reg. (MDR) ou que será escrito na memória
Acumulador ACC Armazena resultados intermediários da ULA
(em arquiteturas simples)
Stack SP Aponta para o topo da pilha na memória
Pointer
Em processadores reais, há muito mais registradores — o MIPS tem 32 registradores de propósito geral, o x86-64 tem 16 registradores de 64 bits, e o ARMv8 também tem 31 registradores gerais. Mas o modelo conceitual com PC, IR, MAR e MBR é o que precisamos para entender o ciclo de execução.
O ciclo fetch-decode-execute
O coração da máquina de Von Neumann é um loop infinito com três etapas. Todo processador, do mais simples ao mais complexo, implementa alguma variação deste ciclo:
// Simulação simplificada do ciclo Von Neumann
PC = 0x0000 // endereço da primeira instrução
executando = true
enquanto executando:
// ─── FETCH (busca) ───────────────────────────
MAR = PC // coloca endereço no MAR
MBR = memoria[MAR] // lê instrução da memória
IR = MBR // copia para o registrador de instrução
PC = PC + 4 // avança PC para a próxima instrução
// (+4 porque cada instrução tem 4 bytes)
// ─── DECODE (decodificação) ──────────────────
opcode = IR[31:26] // bits 31-26: qual operação
rs = IR[25:21] // bits 25-21: registrador fonte 1
rt = IR[20:16] // bits 20-16: registrador fonte 2
rd = IR[15:11] // bits 15-11: registrador destino
operacao = decodifica(opcode)
// ─── EXECUTE (execução) ──────────────────────
se operacao == ADD:
REG[rd] = REG[rs] + REG[rt]
se operacao == LW: // load word
MAR = REG[rs] + offset
MBR = memoria[MAR]
REG[rt] = MBR
se operacao == BEQ: // branch if equal
se REG[rs] == REG[rt]:
PC = PC + (offset × 4)
se operacao == HALT:
executando = false
Cada passo em detalhe:
Fetch (busca): O PC contém o endereço da próxima instrução. Esse endereço é colocado no MAR e enviado à memória. A memória retorna o conteúdo daquele endereço no MBR. A instrução é copiada para o IR. O PC é incrementado (normalmente em 4 bytes, o tamanho de uma instrução em arquiteturas de 32 bits).
Decode (decodificação): A unidade de controle examina os bits da instrução no IR. Os primeiros bits (opcode) dizem qual operação realizar. Os bits restantes indicam os operandos — registradores fonte, registrador destino, valores imediatos, ou endereços de memória.
Execute (execução): A operação decodificada é realizada. Se é uma operação aritmética, a ULA calcula o resultado. Se é uma instrução de acesso à memória (load/store), a memória é lida ou escrita. Se é um branch, o PC é modificado para apontar para outro endereço.
O gargalo de Von Neumann
A arquitetura Von Neumann tem uma fraqueza estrutural: dados e instruções compartilham a mesma memória e o mesmo barramento. Isso significa que o processador não pode buscar uma instrução e ler/escrever um dado ao mesmo tempo — eles competem pelo mesmo caminho.
CPU Memória
┌───────┐ ┌────────┐
│ │ ←── busca ──→ │ instr. │
│ │ instrução │ │ ← mesmo barramento!
│ │ ←── lê/escreve│ dados │
│ │ dado │ │
└───────┘ └────────┘
Problema: a CPU é muito mais rápida que a memória.
Ela fica ociosa esperando dados/instruções chegarem.
Em um processador moderno a 4 GHz:
Ciclo de clock: ~0.25 ns
Acesso à RAM: ~100 ns (400x mais lento!)
A CPU pode executar centenas de operações no tempo
que leva para ler um dado da memória principal.
Esse problema é chamado de Von Neumann Bottleneck (gargalo de Von Neumann), termo cunhado por John Backus em 1977. À medida que processadores ficaram mais rápidos que a memória (a "memory wall"), o gargalo se tornou o principal limitador de desempenho.
As soluções que a indústria desenvolveu para mitigar o gargalo incluem:
- Cache — múltiplos níveis de memória rápida (L1, L2, L3) entre a CPU e a RAM, que armazenam os dados/instruções usados recentemente
- Pipeline — sobrepor a busca da próxima instrução com a execução da atual
- Prefetch — buscar dados da memória antes que o programa precise deles
- Barramentos mais largos — transferir mais bits por ciclo (de 8 para 16, 32, 64, 128 bits)
- Execução fora de ordem — executar instruções que não dependem do dado esperado enquanto a memória responde
Arquitetura Harvard
Uma alternativa ao modelo Von Neumann é a arquitetura Harvard, que usa memórias e barramentos separados para instruções e dados:
Von Neumann: Harvard:
CPU ←→ [barramento] ←→ Memória CPU ←→ [barram. instr.] ←→ Memória Instr.
(único) (única) CPU ←→ [barram. dados] ←→ Memória Dados
(dois barramentos, duas memórias)
Von Neumann: simples, flexível Harvard: mais rápida (acesso simultâneo),
(qualquer % de instruções/dados) mas menos flexível (tamanhos fixos)
Na prática, a maioria dos processadores modernos usa uma arquitetura Harvard modificada: a cache L1 é separada em cache de instruções (I-cache) e cache de dados (D-cache) — modelo Harvard. Mas a memória principal é unificada — modelo Von Neumann. Isso combina a velocidade do acesso separado com a flexibilidade da memória unificada.
Microcontroladores usados em sistemas embarcados (como os da família AVR, usados no Arduino) frequentemente usam Harvard puro: memória Flash para instruções e SRAM para dados, com barramentos completamente separados.
No harness.os
O ciclo fetch-decode-execute é um loop de processamento — o mesmo padrão que uma session no harness.os segue: buscar contexto, decodificar a tarefa, executar a ação, gravar resultado.
Ciclo Von Neumann Ciclo de sessão harness.os
────────────────────────────── ──────────────────────────────────────
FETCH: PC → MAR → Memória FETCH: start_session() → carregar
→ MBR → IR handoff, regras, estado
DECODE: IR → opcode, operandos DECODE: analisar o pedido do usuário,
→ sinais de controle identificar tools necessários,
consultar get_workflow()
EXECUTE: ULA calcula, EXECUTE: chamar MCP tools, editar
memória lê/escreve, arquivos, rodar comandos,
PC atualiza log_decision(), log_learning()
[loop] WRITE-BACK: end_session(summary,
next_steps) → gravar handoff
O gargalo de Von Neumann O gargalo do agente é a
é a latência da memória. latência da context window.
Solução: cache. Solução: prompt cache, knowledge
chunks por domínio.
O gargalo de Von Neumann — CPU rápida esperando memória lenta — tem paralelo direto com o gargalo do agente: o modelo é rápido para processar, mas carregar contexto grande é lento e caro. A solução nos dois casos é a mesma: hierarquia de cache. No harness.os, os knowledge chunks funcionam como cache L1 (dados frequentes, rápidos de carregar), enquanto o banco de dados completo é a "memória principal" (acesso mais lento, capacidade total).
Homework
- Desenhe o diagrama Von Neumann para um computador específico que você usa. Identifique: qual é a CPU (modelo e microarquitetura)? Quanta RAM? Quais dispositivos de entrada e saída estão conectados? Quais barramentos (USB, PCIe, NVMe)?
- Trace manualmente o ciclo fetch-decode-execute para as seguintes três instruções MIPS, começando com PC = 0x0000 e registradores $t0 = 5, $t1 = 3:
Para cada instrução, escreva os valores de PC, MAR, MBR, IR e dos registradores relevantes em cada etapa (fetch, decode, execute).Assembly MIPS
0x0000: add $t2, $t0, $t1 # $t2 = $t0 + $t1 0x0004: addi $t3, $t2, 10 # $t3 = $t2 + 10 0x0008: sw $t3, 0($zero) # memoria[0] = $t3 - Calcule o impacto do gargalo de Von Neumann: se um processador a 4 GHz precisa acessar a RAM (100 ns de latência) a cada 10 instruções, quantos ciclos ele perde esperando a memória em 1 segundo de execução?
- No harness.os, identifique um exemplo de "gargalo de Von Neumann" — uma situação onde o agente fica esperando dados (contexto) chegarem em vez de estar processando. Como você resolveria esse gargalo com "caching"?
Resumo
- O stored-program concept de Von Neumann (1945): instruções e dados ficam na mesma memória — o programa é dado
- Cinco componentes: ULA (calcula), UC (coordena), memória (armazena), entrada e saída
- Registradores-chave: PC (endereço da próxima instrução), IR (instrução atual), MAR (endereço de memória), MBR (dado lido/escrito)
- O ciclo fetch-decode-execute é o loop fundamental: buscar instrução, decodificar, executar, repetir
- O gargalo de Von Neumann: CPU rápida × memória lenta = CPU ociosa esperando dados. Mitigado por cache, pipeline, prefetch
- A arquitetura Harvard separa memórias e barramentos de instruções e dados; processadores modernos usam Harvard modificada (caches separadas, memória principal unificada)
Verifique seu entendimento
Qual é a causa principal do "gargalo de Von Neumann" (Von Neumann Bottleneck)?