Latencia vs Rendimiento

Las CPUs están optimizadas para la latencia — ejecutar un único hilo de instrucciones lo más rápido posible. Invierten transistores en predicción de saltos, ejecución especulativa, pipelines fuera de orden y cachés grandes (a menudo más del 50% del área del chip). El resultado es que un hilo se ejecuta muy rápido, pero el paralelismo disponible es limitado.

Las GPUs hacen la apuesta opuesta: rendimiento (throughput) . Sacrifican el rendimiento de un solo hilo a cambio de paralelismo masivo. En lugar de hacer que un hilo sea rápido, ejecutan miles de hilos simultáneamente, cada uno más lento individualmente pero colectivamente procesando mucho más trabajo por segundo. Como describe el Modal GPU Glossary , las GPUs son procesadores orientados al rendimiento — están diseñadas para maximizar la cantidad total de trabajo completado por unidad de tiempo en lugar de minimizar el tiempo que tarda una sola tarea.

Una analogía útil: una CPU es como un auto deportivo — muy rápido para un solo pasajero. Una GPU es como un autobús — más lento por pasajero, pero mueve a 50 personas a la vez. Para cargas de trabajo que procesan millones de puntos de datos independientes (píxeles, elementos de matrices, embeddings de tokens), el autobús gana de manera decisiva.

Podemos ilustrar esta ventaja de rendimiento con una comparación sencilla. Un bucle for de Python procesa elementos uno a la vez (como una CPU ejecutando secuencialmente), mientras que una operación vectorizada de numpy aplica la misma transformación a todos los elementos simultáneamente (análogo a lo que hace una GPU de forma nativa).

import numpy as np
import time

# Simulate: CPU-style sequential vs GPU-style parallel
n = 1_000_000
data = np.random.randn(n).astype(np.float32)

# "Sequential" (one element at a time)
start = time.time()
result_seq = np.array([x * 2.0 + 1.0 for x in data])
seq_time = time.time() - start

# "Parallel" (vectorised — this is what GPUs do natively)
start = time.time()
result_par = data * 2.0 + 1.0
par_time = time.time() - start

print(f"Sequential (Python loop): {seq_time:.3f}s")
print(f"Parallel (numpy vectorised): {par_time:.4f}s")
print(f"Speedup: {seq_time/par_time:.0f}×")
print(f"\nThis is a simplified analogy for the CPU vs GPU difference:")
print(f"GPUs apply the SAME operation to MANY data points simultaneously.")

La ruta vectorizada es típicamente 50–200× más rápida, incluso en una CPU — y esto solo insinúa la escala de paralelismo que proporciona una GPU real. Una H100, por ejemplo, puede sostener más de 16,000 hilos ejecutando aritmética de punto flotante en cada ciclo de reloj.

💡 Para una referencia completa sobre conceptos de hardware GPU, consulta el Modal GPU Glossary (https://modal.com/gpu-glossary), que cubre hardware del dispositivo, software y rendimiento en profundidad. Este track sintetiza esos conceptos en una ruta de aprendizaje estructurada.

La Jerarquía Física

Una GPU está organizada en una jerarquía anidada de unidades de procesamiento. Esta jerarquía existe debido a restricciones físicas fundamentales — no se puede conectar cada núcleo directamente a cada byte de memoria. En su lugar, el chip agrupa núcleos a escalas progresivamente mayores, con cada nivel proporcionando sus propios recursos de memoria y planificación. De arriba hacia abajo, la jerarquía se ve así:

  • GPC (Graphics/GPU Processing Cluster): el bloque de procesamiento más grande. Una H100 tiene 8 GPCs. Cada GPC es un cluster independiente con su propio motor de rasterización e interfaz de memoria.
  • TPC (Texture Processing Cluster): cada GPC contiene múltiples TPCs (típicamente 2 en arquitecturas modernas). Un TPC agrupa Streaming Multiprocessors para propósitos de planificación.
  • SM (Streaming Multiprocessor): la unidad de cómputo central. Una H100 tiene 132 SMs en total. Cada SM contiene CUDA Cores, Tensor Cores, cachés y un archivo de registros. Aquí es donde ocurre la matemática real.
  • Núcleos: dentro de cada SM se encuentran las unidades de ejecución — CUDA Cores (para matemáticas generales de punto flotante), Tensor Cores (para operaciones matriciales), Unidades de Funciones Especiales o SFUs (para sin , cos , exp ), y Unidades de Carga/Almacenamiento o LSUs (para acceso a memoria).

El siguiente diagrama muestra esta jerarquía para la NVIDIA H100 (consulta el Modal GPU Glossary para detalles adicionales sobre cada componente):

GPU (e.g., H100)
├── GPC 0                          ← 8 GPCs
│   ├── TPC 0                     ← 2 TPCs per GPC (typ.)
│   │   ├── SM 0                  ← ~8 SMs per GPC
│   │   │   ├── 128 CUDA Cores    ← FP32 arithmetic
│   │   │   ├── 4 Tensor Cores    ← matrix multiply-accumulate
│   │   │   ├── 4 SFUs            ← sin, cos, exp, rsqrt
│   │   │   ├── 32 LSUs           ← load/store from memory
│   │   │   ├── 4 Warp Schedulers ← pick warps to execute
│   │   │   ├── 256 KB Register File  ← per-thread fast storage
│   │   │   └── 256 KB L1/Shared Memory
│   │   └── SM 1 ...
│   └── TPC 1 ...
├── GPC 1 ...
├── ...
├── L2 Cache (50 MB on H100)
└── HBM3 GPU RAM (80 GB on H100, ~3 TB/s bandwidth)

La estructura anidada sirve dos propósitos. Primero, proporciona memoria progresivamente más grande pero más lenta en cada nivel: registros (más rápidos, por hilo) → L1/memoria compartida (por SM) → caché L2 (a nivel de chip) → HBM (fuera del chip, mayor capacidad). Segundo, agrupa núcleos para una planificación eficiente — un planificador de warps dentro de un SM puede despachar trabajo a sus núcleos locales sin coordinarse con el resto del chip.

Streaming Multiprocessors: La Unidad de Cómputo Central

El SM es donde ocurre toda la computación. Piensa en él como un procesador en miniatura, pero uno diseñado de manera muy diferente a un núcleo de CPU.

Un núcleo de CPU es complejo: predice saltos, reordena instrucciones, especula sobre trabajo futuro y mantiene una jerarquía de caché grande — todo para hacer que un único hilo se ejecute lo más rápido posible. Un SM es más simple: ejecuta instrucciones en orden, no predice saltos y tiene una caché relativamente pequeña. En su lugar, gestiona hasta 2,048 hilos concurrentes (64 warps de 32 hilos cada uno) y oculta la latencia de memoria cambiando entre warps en cada ciclo de reloj.

Cuando un warp está esperando datos de la memoria — una latencia que puede tomar cientos de ciclos — el SM cambia inmediatamente a otro warp que tiene datos listos. Esta técnica, a menudo llamada ocultamiento de latencia mediante ocupación , es el truco fundamental que hace que las GPUs sean rápidas. No hacen la memoria más rápida; simplemente realizan trabajo útil mientras esperan. El Modal GPU Glossary describe esto como la respuesta de la GPU al muro de memoria: en lugar de reducir la latencia, la tolera manteniendo suficiente trabajo en vuelo para que siempre haya algo que ejecutar.

Aquí están los números concretos para la NVIDIA H100:

  • 132 SMs × 128 CUDA Cores = 16,896 CUDA Cores en total
  • 132 SMs × 4 Tensor Cores = 528 Tensor Cores en total
  • 132 SMs × 2,048 hilos máximos = 270,336 hilos concurrentes
  • Cada SM: 256 KB de archivo de registros, 256 KB de L1/memoria compartida

Compara esto con una CPU de gama alta como el AMD EPYC 9654 — tiene 96 núcleos con 2 hilos cada uno, para un total de 192 hilos concurrentes. La H100 gestiona aproximadamente 1,400× más hilos concurrentes. Individualmente cada hilo de GPU es mucho más simple y lento, pero el rendimiento agregado es lo que importa para cargas de trabajo con paralelismo de datos.

💡 El SM se cubre en profundidad en el artículo 2. La conclusión clave aquí: un SM no es un núcleo de CPU. Es más simple individualmente pero gestiona órdenes de magnitud más hilos concurrentes, y su rendimiento proviene de cambiar entre ellos en lugar de hacer que cualquiera de ellos sea rápido.

Por Qué Esto Importa para el Deep Learning

El entrenamiento e inferencia de redes neuronales están dominados por multiplicaciones de matrices — y la multiplicación de matrices es vergonzosamente paralela . Cada elemento de salida es un producto punto independiente que puede calcularse sin esperar a ningún otro elemento.

Considera una multiplicación de matrices de dos matrices de 4096×4096. Esto involucra aproximadamente $2 \times 4096^3 \approx 137$ mil millones de operaciones de multiplicación-acumulación en FP16. En una CPU, esto podría tomar cientos de milisegundos. En una H100 con 528 Tensor Cores ejecutando a aproximadamente 1,000 TFLOPS (FP16), puede completarse en menos de un milisegundo. Esa es la ventaja de rendimiento en la práctica.

Veamos esto con un ejemplo pequeño. Multiplicaremos dos matrices pequeñas y verificaremos que cada elemento de salida es un producto punto independiente — exactamente el tipo de trabajo que se mapea perfectamente en miles de hilos paralelos de GPU.

import numpy as np

# Matrix multiply: each output element is an independent dot product
M, K, N = 4, 3, 2
A = np.random.randn(M, K).round(1)
B = np.random.randn(K, N).round(1)
C = A @ B

print(f"A ({M}×{K}) @ B ({K}×{N}) = C ({M}×{N})")
print(f"\nA:\n{A}")
print(f"\nB:\n{B}")
print(f"\nC = A @ B:\n{C}")
print(f"\nEach element of C is an independent dot product:")
for i in range(M):
    for j in range(N):
        dot = sum(A[i,k] * B[k,j] for k in range(K))
        print(f"  C[{i},{j}] = {' + '.join(f'{A[i,k]:.1f}×{B[k,j]:.1f}' for k in range(K))} = {dot:.2f}")

total_ops = M * N * (2 * K - 1)
print(f"\nTotal operations: {total_ops} (all independent → perfect for GPUs)")
print(f"For 4096×4096 matmul: ~{2 * 4096**3 / 1e9:.0f} billion ops")

En cargas de trabajo reales de deep learning, este patrón se repite en cada capa de la red. Un solo paso forward a través de un modelo de lenguaje grande puede involucrar cientos de multiplicaciones de matrices, cada una con dimensiones en los miles. Sin el paralelismo masivo de una GPU, el deep learning moderno simplemente no sería práctico — ejecuciones de entrenamiento que toman días en GPUs tomarían años en CPUs.

Quiz

Pon a prueba tu comprensión de los fundamentos de la arquitectura GPU — el modelo de rendimiento, la jerarquía física y por qué las GPUs son adecuadas para cargas de trabajo de deep learning.

¿Cuál es la diferencia de diseño fundamental entre un núcleo de CPU y un SM de GPU?

¿Cómo oculta la latencia de memoria un Streaming Multiprocessor (SM) de GPU?

En la jerarquía física de la GPU, ¿qué componentes contiene un Streaming Multiprocessor (SM)?

¿Por qué la multiplicación de matrices es particularmente adecuada para la ejecución en GPU?