English · Español
04 — Roofline de GPU¶
La ecuación del roofline (
perf = min(π, I × β)) es la misma que en Fase 1 — pero ahora con múltiples ceilings (uno por dtype: fp64, fp32, fp16/bf16/fp8) y un machine balance entre 50 y 300 FLOPs/byte. La caché-attention del decode de Fase 22 sigue siendo memory-bound aquí, aún más profundamente que en la CPU.
Esta página es la versión GPU de docs/phase-01-hardware-substrate/theory/03-roofline-model.md. La ecuación es idéntica; las constantes no; las colocaciones de operadores que harás en el lab usarán este plot.
La ecuación, sin cambios¶
- \(\pi\): peak FLOPS (el ceiling de cómputo).
- \(\beta\): peak bandwidth (la pendiente del ceiling de memoria).
- \(I = F / B\): intensidad aritmética (FLOPs por byte).
Igual que en la Fase 1. Única diferencia: en una GPU, \(\pi\) depende fuertemente del dtype, mucho más que en CPU.
Múltiples ceilings, un solo plot¶
Una GPU moderna tiene cifras de pico de rendimiento separadas para:
- fp64 (CUDA cores)
- fp32 (CUDA cores)
- TF32 (Tensor Cores; ~16× fp32)
- fp16 / bf16 (Tensor Cores; ~16× fp32)
- fp8 (Tensor Cores; ~32× fp32, H100+)
- int8 (Tensor Cores; mismo rendimiento que fp8)
- int4 (Tensor Cores; H100+)
Por ejemplo, una A100:
| Dtype | Peak TFLOPS (Tensor Cores) | Peak TFLOPS (CUDA cores) |
|---|---|---|
| fp64 | 19.5 (denso) | 9.7 |
| fp32 / TF32 | 156 (TF32) | 19.5 |
| fp16 / bf16 | 312 | 78 |
| fp8 | N/A (H100+) | N/A |
| int8 | 624 | 312 |
Ancho de banda HBM: 2 TB/s (A100 PCIe), 1.55 TB/s (A100 SXM4 40GB), hasta 2 TB/s (A100 SXM4 80GB).
Cada dtype da una línea de ceiling de cómputo distinta en el roofline. Todas comparten la misma pendiente de ceiling de memoria (el ancho de banda de HBM es agnóstico al dtype — mueve bytes, no FLOPs). Así que el plot se ve así:
TFLOPS (log)
^
│ ╭───────── ← fp8/int8 Tensor Cores (624)
│ ╱
│ ╭───────────── ← fp16/bf16 TC (312)
│ ╱
│ ╭───────────────── ← TF32 TC (156)
│ ╱
│ ╭──────────────────── ← fp32 CUDA cores (19.5)
│ ╱
│ ╭────────────────────────── ← fp64 (9.7)
│ ╱
│ memory ceiling slope = β = 2 TB/s
│ (same for every dtype)
│ ╱
│ ╱
└──────────────────────────────────────────→
I (FLOPs/byte, log)
Las esquinas (\(I_\text{crit}\) para cada dtype) se desplazan a la derecha conforme sube el ceiling de cómputo:
- fp8: \(I_\text{crit} = 624 / 2 = 312\) FLOPs/byte
- fp16: \(I_\text{crit} = 156\)
- fp32 (CUDA core): \(I_\text{crit} = 9.75\)
- fp64: \(I_\text{crit} = 4.85\)
Un kernel a \(I = 1\) FLOP/byte (decode attention fp16) está sobre la pendiente de memoria para todos los dtypes — muy por debajo de todas las esquinas. Memory-bound independientemente del dtype.
Un kernel a \(I = 20\) FLOPs/byte es: - memory-bound para fp16 Tensor Cores (debajo de \(I_\text{crit} = 156\)). - compute-bound para fp32 CUDA cores (encima de \(I_\text{crit} = 9.75\)).
Mismo kernel, régimen distinto según la precisión. Por eso "usa fp16" no siempre ayuda — para kernels bien dentro del régimen compute-bound, fp16 solo divide entre dos el tráfico de memoria sin subir el pico que en realidad estás golpeando. Para kernels en el régimen memory-bound, fp16 duplica la intensidad (divide entre dos los bytes por FLOP) y da un acelerón cercano a 2×.
Re-colocando los operadores de la Fase 22 sobre el roofline de GPU¶
Ahora el pago. Los mismos operadores que colocamos sobre el roofline de CPU en la Fase 22 aterrizan aquí sobre el plot de GPU:
Prefill attention (\(P \times P\))¶
- FLOPs por capa: \(2 P^2 d\). Para Llama-2-7B fp16 (\(d=4096\)), \(P=2048\): \(\approx 7 \cdot 10^{10}\) FLOPs/capa × 32 capas = \(2.2 \cdot 10^{12}\) FLOPs.
- Bytes (sin Flash-Attention, materializando \(P \times P\)): \(\sim 4 P^2 \cdot 2\) bytes para la matriz de attention fp16 + las lecturas de K, V. El working set es grande; se vuelca a HBM.
- Intensidad: dominada por el tráfico de la matriz de attention. Para \(P=2048\), \(I \approx P = 2048\) — profundamente compute-bound. Se sitúa sobre el ceiling fp16 TC.
- Con Flash-Attention (lab de la Fase 24): la matriz de attention nunca se materializa; el working set se queda en SMEM; la intensidad sube aún más. Mismo cómputo, menos bytes, mismo ceiling — pero ahora el kernel puede realmente golpearlo (Flash-Attention alcanza ~75% del pico fp16 TC en la práctica).
Decode-step attention (\(1 \times S\) por capa)¶
- FLOPs por capa por step: \(\approx 4 S d\). Para Llama-2-7B, \(S=4096\): \(4 \cdot 4096 \cdot 4096 = 6.7 \cdot 10^7\) FLOPs/capa/step. Por 32 capas = \(2.1 \cdot 10^9\) FLOPs/step.
- Bytes por capa por step: \(2 S d \cdot s\) para la lectura de cache. \(S=4096\), \(s=2\): \(6.7 \cdot 10^7\) bytes/capa/step. Por 32 capas = \(2.1 \cdot 10^9\) bytes/step.
- Intensidad: \(2.1 \cdot 10^9 / 2.1 \cdot 10^9 = 1\) FLOP/byte. Como se derivó en la Fase 22.
- Coloca sobre el roofline: \(I = 1\), muy a la izquierda de toda esquina. Memory-bound en \(\text{perf} = 1 \cdot 2\text{ TB/s} = 2\) TFLOPS. Fracción del pico fp16 TC: 2 / 312 = 0.6%. Las FPUs están al 99.4% ociosas durante decode attention. Mismo diagnóstico que en CPU, con números absolutos aún más duros.
Decode-step FFN¶
- FLOPs por capa por step: \(24 d^2\) (dos matmuls). Para Llama-2-7B: \(24 \cdot 4096^2 = 4 \cdot 10^8\) FLOPs/capa/step. Por 32 = \(1.3 \cdot 10^{10}\) FLOPs/step.
- Bytes por capa por step: \(\approx 12 d^2 s\) (lectura de pesos). \(12 \cdot 4096^2 \cdot 2 = 4 \cdot 10^8\) bytes/capa/step. Por 32 = \(1.3 \cdot 10^{10}\) bytes/step = 13 GiB leídos por token.
- Intensidad: \(1.3 \cdot 10^{10} / 1.3 \cdot 10^{10} = 1\) FLOP/byte. Igual que decode attention. Memory-bound a 2 TFLOPS.
- Tiempo a 2 TB/s HBM: 13 GiB / 2000 GB/s ≈ 6.5 ms / token. Éste es el suelo para decode single-stream en A100. Las mediciones reales caen en 10–15 ms — cerca de la cota.
Decode-step FFN, con batch (\(B\) secuencias)¶
- FLOPs por capa por step: \(24 B d^2\).
- Bytes por capa por step: \(\approx 12 d^2 s\) (pesos leídos una vez, aplicados a \(B\) filas).
- Intensidad: \(2B / s = B\) (para fp16). Con \(B=16\): \(I = 16\). Aún memory-bound para fp16 TC (\(I_\text{crit} = 156\)). Hace falta \(B \geq 156\) para volverse compute-bound para FFN fp16 TC, lo cual es impráctico (KV cache para 156 secuencias concurrentes = 312 GiB a contexto 4k — muy por encima de una sola GPU). El batching realista de decode (\(B \in [16, 64]\)) se queda memory-bound pero gana una mejora de rendimiento \(B\)-fold.
Prefill GEMM (el matmul FFN grande en prefill)¶
- \(P \times d \cdot d \times 4d\). FLOPs: \(8 P d^2\). Bytes: \(\sim 12 P d^2 s\) (cargando X, pesos). Intensidad: \(\sim 2/s = 1\) para fp16 — espera, eso es lo mismo que decode. ¿Por qué no es prefill memory-bound también?
- Respuesta: en prefill procesas los \(P\) tokens a la vez; el matmul es \(P\)-dimensional, como decode-con-batch-\(P\). Intensidad efectiva = \(\sim 2 P / s = P\). Para \(P = 2048\) fp16: \(I = 2048\) — profundamente compute-bound. El coste vuelve a "FLOPs de matmul en fp16 TC", pico ~312 TFLOPS.
Por esto prefill es rápido (compute-bound, golpeando cerca del pico fp16 TC) y decode es lento (memory-bound, ~1% del pico de cómputo). La asimetría en la GPU es más extrema que en la CPU porque el ratio compute-to-bandwidth de la GPU es muy alto.
El plot que commitearás al final del lab¶
El plot del roofline de GPU (experiments/23-roofline-gpu/roofline.png) debe parecerse al esquema de arriba, con:
- Cinco ceilings de cómputo (uno por dtype relevante).
- Un ceiling de memoria (en pendiente).
- Al menos cuatro puntos: decode-attention fp16 (I=1), decode-FFN fp16 single-stream (I=1), decode-FFN fp16 batched-16 (I=16), prefill-FFN fp16 (I≈P).
- Una línea horizontal "tu GEMM de cuBLAS medido" indicando el pico realmente alcanzable (típicamente 70–90% del pico de fabricante).
Este único plot es el mapa de operadores para el resto del trabajo de inferencia. La Fase 24 moverá puntos hacia arriba por la pendiente (optimización de kernel) o a la derecha (bajando precisión). La Fase 27 reorganizará el layout de memoria para mover bytes. La Fase 33 hará batching para subir la intensidad efectiva. Todo movimiento tiene una descripción en vocabulario de roofline.
Cómo se relaciona con "las specs de fabricante mienten"¶
Cuando NVIDIA cita "989 TFLOPS para H100 fp16", se refiere a pico denso fp16 de Tensor Core. Ese número asume:
- Tensor Cores totalmente alimentados (no CUDA cores).
- Sin stalls por dependencias.
- Los 132 SMs activos.
- Sin segmentos memory-bound.
Un modelo real alcanza el 30–70% de ese número, dependiendo del operador. GEMM de cuBLAS a tamaños grandes (p.ej., matmul 8192×8192) llega a ~80%. Cualquier cosa memory-bound llega a <5%.
Cuando lees "H100 es 6× más rápido que A100", lo que se quiere decir es: \(\pi_\text{H100} / \pi_\text{A100} = 989/312 \approx 3.2\times\) para fp16 denso, y en cuanto a ancho de banda \(\beta_\text{H100} / \beta_\text{A100} \approx 1.6\times\). Para trabajo compute-bound, ganas ~3.2×. Para trabajo memory-bound (decode), ganas ~1.6×. La mayoría de la inferencia LLM es memory-bound, así que el acelerón práctico está más cerca de 1.6× por GPU, no 6×.
Un usuario que mire un benchmark de fabricante que muestre "H100 = 6× A100 para inferencia LLM" debería preguntar: ¿qué operador están midiendo? Si es prefill / entrenamiento (compute-bound), el 6× es real. Si es decode (memory-bound), el 6× requiere también batchar muy fuerte (para que las lecturas de pesos se amorticen, llevando la intensidad efectiva al régimen compute-bound).
Ésta es la lección más profunda del roofline: el rendimiento tiene forma de operador, no forma de chip.
Problemas de práctica¶
- Calcula el machine balance para fp16 Tensor Cores en H100 (\(\pi \approx 989\) TFLOPS, \(\beta \approx 3.35\) TB/s).
- La decode attention de la Fase 22 a fp16 en H100: colócala sobre el roofline fp16 TC de H100. ¿Rendimiento alcanzable? ¿Fracción del pico?
- Un kernel custom de Flash-decoding sube la intensidad efectiva de la decode attention (manteniendo el working set en SMEM más tiempo) en ~4×. ¿Adónde se mueve el punto? ¿Factor de acelerón?
- El mismo kernel, con cache cuantizada (int8 en vez de fp16). Los bytes se reducen a la mitad; los FLOPs no cambian. ¿Nueva intensidad? ¿Nuevo rendimiento alcanzable?
Lo que ahora deberías ser capaz de hacer¶
- Esbozar el roofline multi-dtype de GPU de memoria.
- Colocar correctamente cualquier operador de la Fase 22 sobre él.
- Predecir el acelerón de una optimización propuesta (Flash, cuantización, batching) calculando el cambio de intensidad.
- Leer una afirmación de fabricante ("X es 5× más rápido que Y") y descomponerla en componentes de cómputo y de memoria.
Lo que esta página NO cubre¶
- Ceilings de potencia / TDP. El silicio real throttla en pico sostenido; los rooflines aquí son nominales.
- Sparse Tensor Cores (sparsity 2:4). Duplican el pico para pesos sparse-estructurados; el survey de cuantización de la Fase 26 lo menciona, deep dive en Fase 27+.
- Rooflines multi-GPU (NVLink como tercer ceiling). Fase 35.
Siguiente: lab/00-provision-cloud-gpu.md. El modelo mental está construido; toca alquilar el hardware.