🖥️ Cómo funciona una computadora
De los bits a la ejecución: binario, representación de datos, arquitectura von Neumann, la CPU y su ciclo, jerarquía de memoria, ensamblador, compilación y optimización. Teoría y práctica.
Cómo funciona una computadora
Todo el código que escribes —Python, Go, PHP, SQL— termina ejecutándose como instrucciones de máquina sobre una CPU. Esta página te enseña qué pasa realmente: cómo se guardan los datos en bits, cómo la CPU los procesa, y qué es la jerarquía de memoria. Es la base que explica por qué un programa es rápido o lento, y por qué un byte no es un carácter.
Bits, bytes y sistema binario
Una computadora trabaja con bits: un bit es un 0 o un 1 (una diferencia de voltaje en un transistor). 8 bits = 1 byte. Con 1 byte representas 256 valores (2⁸).
1 byte = 8 bits: 1 0 1 1 0 0 0 1 → 177 en decimal
bits: 128 64 32 16 8 4 2 1
1 0 1 1 0 0 0 1 = 128+32+16+1 = 177
| Unidad | Tamaño |
|---|---|
| 1 bit | 0 o 1 |
| 1 byte | 8 bits (256 valores) |
| 1 KB | 1024 bytes |
| 1 MB | 1024 KB ≈ 1 millón de bytes |
| 1 GB | 1024 MB ≈ 1.000 millones de bytes |
💡 ¿Por qué 1024 y no 1000? Porque el hardware usa potencias de 2: 2¹⁰ = 1024. La industria de discos duros usa 1000 (por eso un disco “de 1 TB” muestra ~931 GB en el sistema).
Representación de números y caracteres
- Enteros: binario directo. Con
nbits representas de-2^(n-1)a2^(n-1)-1(si usas signo). El complemento a dos es cómo se representan los negativos (el bit más significativo es el signo). - Flotantes: se guardan en IEEE 754: signo + exponente + mantisa. Por eso
0.1 + 0.2 ≠ 0.3(visto en Programación). - Caracteres: un carácter es un número. ASCII usa 7 bits (128 caracteres:
A=65,a=97). UTF-8 es el estándar moderno: usa 1-4 bytes por carácter y es compatible con ASCII.
'A' = 65 decimal = 0x41 = 01000001 binario
'ñ' en UTF-8 = 2 bytes: 11000011 10110001
⚠️ Terminología:
0x41es hexadecimal (base 16, dígitos 0-9 y A-F). Los ingenieros usan hex porque 1 dígito hex = 4 bits exactos.0b1011es binario,0101octal. En C/Go/JS:0x41, en Pythonhex(65).
La arquitectura de von Neumann
Casi toda computadora moderna sigue el modelo de von Neumann: una unidad central de procesamiento (CPU) conectada a una memoria que guarda tanto los datos como el programa.
┌─────────────────────────────────────┐
│ CPU │
│ ┌───────┐ ┌───────┐ ┌──────────┐ │
Datos ◀───────▶ │ ALU │ │Regis- │ │ Control │ │
│ │(opera)│ │tros │ │ (CU) │ │
│ └───────┘ └───────┘ └──────────┘ │
└──────┬──────────────────────┬────────┘
│ (bus de datos/dir.) │
┌──────▼──────────────────────▼────────┐
│ MEMORIA (RAM) │
│ programa (instrucciones) + datos │
└───────────────────────────────────────┘
- ALU (Unidad Aritmético-Lógica): hace las operaciones (sumar, restar, comparar, AND/OR).
- Registros: almacenamiento ultrarrápido dentro de la CPU (decenas de bytes).
- CU (Unidad de Control): decodifica las instrucciones y coordina todo.
- Reloj: cada «tic» permite una etapa del procesamiento. La velocidad se mide en GHz (miles de millones de tics por segundo).
El ciclo de ejecución (fetch-decode-execute)
La CPU ejecuta las instrucciones en un ciclo infinito:
- Fetch (buscar): lee la siguiente instrucción de la memoria (apuntada por el PC — Program Counter / puntero de instrucción).
- Decode (decodificar): descifra qué instrucción es y qué operandos usa.
- Execute (ejecutar): la ALU hace la operación, escribe el resultado.
PC → memoria: leer instrucción → decodificar → ejecutar → PC++ → repetir
Cada instrucción de máquina es trivialísima: «suma el registro 1 al registro 2». Un programa es millones de estas instrucciones ejecutadas por segundo.
Lenguaje ensamblador y lenguaje de máquina
La CPU solo entiende lenguaje de máquina: bytes (opcodes) que codifican operaciones. El ensamblador es la representación legible de ese lenguaje.
Este código C:
int suma(int a, int b) {
return a + b;
}
Se compila a ensamblador x86-64 así:
suma:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi ; a
mov DWORD PTR [rbp-8], esi ; b
mov eax, DWORD PTR [rbp-4]
add eax, DWORD PTR [rbp-8] ; eax = a + b
pop rbp
ret ; devuelve eax
💡 Los registros son los «dedos» de la CPU:
eax,rbp,rsp… El compilador decide cómo encajar tu código en los registros y la memoria. Puedes verlo tú mismo:gcc -S programa.cgenera el.scon el ensamblador.
Instrucciones fundamentales
| Tipo | Ejemplos | Qué hace |
|---|---|---|
| Movimiento de datos | mov, load, store |
Copiar entre memoria y registros |
| Aritmética | add, sub, mul, imul |
Operaciones matemáticas |
| Lógica | and, or, xor, shl |
Operaciones de bits |
| Control | jmp, jne, call, ret |
Saltar, decidir, llamar funciones |
| Comparación | cmp, test |
Comparar (fija banderas/eflags) |
ISA: el contrato del hardware
Una ISA (Instruction Set Architecture) es el contrato entre el software y el hardware: qué instrucciones existen. El mismo código fuente se compila distinto para cada ISA.
| ISA | Dónde vive |
|---|---|
| x86-64 | CPUs Intel/AMD (la mayoría de servidores y PCs) |
| ARM64 / AArch64 | Móviles, M1/M2/M3 de Apple, Nube (Graviton) |
| RISC-V | Open source, creciendo en todo |
⚠️ CISC vs RISC: x86 es CISC (instrucciones complejas y largas); ARM/RISC-V son RISC (instrucciones simples y uniformes). RISC permite pipeline y consumo eficiente — por eso domina móviles y la nube moderna.
La jerarquía de memoria
La memoria no es un solo bloque: es una pirámide de velocidades y tamaños. Lo más rápido es lo más caro y pequeño.
Velocidad Capacidad
Registros ~1 ciclo ~100 B ← lo más rápido
Caché L1 ~1-4 ciclos ~64 KB
Caché L2 ~10 ciclos ~1 MB
Caché L3 ~40 ciclos ~10-40 MB
RAM ~100 ns ~16-512 GB
SSD/Disk ~0.1-10 ms ~TB ← lo más lento
💡 Regla de oro: la CPU es decenas de miles de veces más rápida que la RAM. Un cache miss (ir a RAM) puede costar lo mismo que cientos de operaciones aritméticas. Los programas rápidos son los que aprovechan la caché: acceso contiguo a la memoria (arrays) en vez de saltos aleatorios (listas enlazadas).
Localidad
Los programas eficientes tienen localidad:
- Localidad temporal: si accedes a un dato, probablemente lo vuelvas a acceder pronto (usa el mismo dato en un bucle).
- Localidad espacial: si accedes a una dirección, probablemente accederás a las vecinas (recorrer un array en orden).
# Lento: acceso salteado (malo para caché)
for i in range(0, n, 64):
total += arr[i]
# Rápido: acceso contiguo (bueno para caché)
for x in arr:
total += x
Compilación y optimización
El compilador traduce tu código de alto nivel a lenguaje de máquina y, de paso, lo optimiza.
gcc -O0 programa.c # sin optimizar (fiel al código)
gcc -O2 programa.c # optimizado (rápido)
gcc -O3 programa.c # optimización agresiva
Optimizaciones clásicas que el compilador hace solas:
- Inlining: sustituye la llamada a una función pequeña por su cuerpo.
- Eliminación de código muerto: borra código que nunca se usa.
- Loop unrolling: duplica el cuerpo del bucle para reducir saltos.
- Reordenación: mueve operaciones para mejorar el pipeline.
⚠️ Por eso medir importa:
-O2puede hacer tu programa 10× más rápido sin tocar una línea. Y por eso los benchmarks mal hechos te engañan (el compilador puede «optimizar» tu código de prueba y eliminarlo entero).
Ejercicio práctico: mira tu código por dentro
- Escribe un programa en C simple y compílalo viendo el ensamblador:
gcc -S -O0 programa.c -o programa.s # ensamblador sin optimizar
gcc -S -O2 programa.c -o programa_o2.s # ensamblador optimizado
diff programa.s programa_o2.s # mira cuánto cambió
- Ejecuta y mide la diferencia:
gcc -O0 programa.c -o p0 && time ./p0
gcc -O3 programa.c -o p3 && time ./p3
- Inspecciona el binario:
file programa # qué tipo de binario es
objdump -d programa | head # desensambla el binario
Terminología que debes dominar
| Término | Significado |
|---|---|
| Bit / byte | Unidad mínima / 8 bits |
| Endianness | Orden de los bytes en memoria (little = byte menos significativo primero; x86 es little) |
| Registro | Almacenamiento interno de la CPU |
| PC / puntero de instrucción | Dónde va la CPU en el programa |
| ISA | Contrato de instrucciones del hardware |
| Ensamblador | Lenguaje legible de la máquina |
| Opcodes | Bytes que codifican las instrucciones |
| Caché | Memoria rápida entre CPU y RAM |
| Cache miss / hit | Fallo / acierto de caché |
| Pipeline | Ejecutar etapas de varias instrucciones en paralelo |
| Clock (reloj) | Tics que sincronizan la CPU (GHz) |
| Overclocking / throttling | Subir / limitar la frecuencia del reloj |
| Little endian | Orden de bytes típico de x86 |
Para profundizar
- CSAPP — Computer Systems: el capítulo 1-3 sobre representación, bits y ensamblador es oro. La biblia de sistemas.
- Nand to Tetris: construyes una computadora completa desde una puerta NAND.
- CMU 15-213: el curso oficial del CSAPP con videos.
- Computer Organization and Design (Patterson/Hennessy): arquitectura a fondo, RISC-V.
- Godbolt Compiler Explorer: ve el ensamblador de tu código en tiempo real en decenas de lenguajes y arquitecturas.
- Memoria y punteros en C: la wiki de este sitio sobre stack, heap y punteros.
- Sigue con ⚙️ Sistemas operativos.