English · Español
03 — Operaciones colectivas, NCCL y matemáticas de coste¶
🇪🇸 Las "primitivas" de la comunicación distribuida son cinco: broadcast, reduce, all-reduce, reduce-scatter, all-gather. Cada estrategia (DDP, ZeRO-N, TP, PP) se compone de estas. Saber su coste — bytes movidos por worker, latencia mínima, dependencia de N — es como saber sumar antes de multiplicar.
Cada estrategia distribuida en theory/01-data-parallel-and-zero.md y theory/02-parallelism-flavors.md está implementada usando un pequeño conjunto de operaciones colectivas. Conocerlas — y sus costes — es lo que te permite predecir si un plan de distribución funcionará antes de ejecutarlo.
Esta página es la mecánica de las cinco colectivas, los algoritmos que NCCL/gloo usan para implementarlas y las matemáticas de ancho de banda que necesitas enchufar en estimaciones tipo roofline.
Las cinco colectivas¶
Para \(N\) workers, cada uno con un tensor \(x_i\) de tamaño \(S\) (bytes):
| Op | Input | Output |
|---|---|---|
| broadcast(src) | El worker src tiene \(x\); los demás no tienen nada |
Todos los workers tienen \(x\) |
| reduce(dst, op) | Cada worker tiene \(x_i\) | El worker dst tiene \(\bigoplus_i x_i\) (p. ej., suma) |
| all-reduce(op) | Cada worker tiene \(x_i\) | Todos los workers tienen \(\bigoplus_i x_i\) |
| reduce-scatter(op) | Cada worker tiene \(x_i\) de tamaño \(S\) | Cada worker tiene \(1/N\) de \(\bigoplus_i x_i\) |
| all-gather | Cada worker tiene \(x_i\) de tamaño \(S/N\) | Todos los workers tienen la concatenación completa de tamaño \(S\) |
Dos identidades no triviales:
- \(\textbf{all-reduce} = \textbf{reduce-scatter} + \textbf{all-gather}\).
- \(\textbf{broadcast} = \textbf{reduce}\) con una única entrada no identidad.
Estas identidades importan porque las implementaciones reales las explotan: un ring all-reduce es un ring reduce-scatter seguido de un ring all-gather.
Ring all-reduce — el algoritmo canónico¶
Los workers forman un anillo lógico: \(0 \to 1 \to 2 \to \ldots \to N-1 \to 0\). El ring all-reduce de un tensor de tamaño \(S\) procede en dos fases de \(N-1\) pasos cada una:
Fase 1: reduce-scatter. El tensor se parte en \(N\) chunks de tamaño \(S/N\). Cada worker \(i\) comienza siendo dueño del chunk \(i\). En el paso \(k\) (para \(k = 0..N-2\)), cada worker manda un chunk a su vecino de la derecha y recibe un chunk de su izquierda, sumando el chunk recibido a su copia local. Tras \(N-1\) pasos, el worker \(i\) tiene el chunk totalmente reducido \((i+1) \mod N\).
Fase 2: all-gather. Mismo anillo, \(N-1\) pasos más. Cada worker pasa su chunk totalmente reducido a la derecha; tras \(N-1\) pasos cada worker tiene los \(N\) chunks totalmente reducidos.
Análisis de coste¶
Por worker, por fase:
- \(N - 1\) pasos, mandando y recibiendo \(S/N\) bytes por paso.
- Bytes totales enviados por worker por fase: \((N-1) \cdot S / N \to S\) para \(N\) grande.
A lo largo de ambas fases, por worker:
- Bytes enviados: \(\approx 2S\) (la línea de manual "2× tamaño de modelo por all-reduce").
- Bytes recibidos: lo mismo.
- Latencia: \(2(N-1)\) pasos. Para \(S\) pequeño, dominado por latencia; para \(S\) grande, dominado por ancho de banda.
¿Por qué anillo? ¿Por qué no árbol?¶
- Tree all-reduce: latencia \(O(\log N)\), ancho de banda por worker \(\approx S\). Más rápido para payloads pequeños, menos eficiente para grandes.
- Ring all-reduce: latencia \(O(N)\), ancho de banda por worker \(\approx 2S\). El "\(2S\)" es independiente de \(N\) asintóticamente — esa es la propiedad que lo hace escalar.
NCCL elige dinámicamente entre ring, tree y double-binary-tree según el tamaño del payload, la topología y el número de workers.
NCCL vs gloo vs MPI¶
| Backend | Dónde | Cuándo |
|---|---|---|
| NCCL | GPUs NVIDIA, NVLink intra-nodo + InfiniBand/RoCE inter-nodo | Por defecto para cualquier trabajo con GPU |
| gloo | CPU, ethernet | Cuando NCCL no está disponible (p. ej., el lab CPU local de Borja) |
| MPI | Cualquier cosa; HPC legacy | Cuando debes integrarte con infraestructura MPI existente |
NCCL tiene comunicación GPU-GPU directa vía NVLink y GPUDirect RDMA — evita la CPU y la memoria del host, lo que es crítico para el rendimiento. Gloo está bien para solo-CPU y es lo que usa experiments/35-ddp-cpu/.
torch.distributed.init_process_group(backend="nccl") en un trabajo GPU; backend="gloo" en CPU.
Matemáticas de ancho de banda de principio a fin¶
Tomemos un ejemplo trabajado: modelo de 7B parámetros, DDP entre \(N = 8\) GPUs, gradientes en fp16.
- \(|\theta| = 7 \cdot 10^9\).
- Tamaño del buffer de gradientes: \(2 |\theta| = 14\) GB.
- Bytes enviados por worker durante el all-reduce: \(2 \cdot 14 = 28\) GB.
El tiempo depende del enlace:
- NVLink 4.0 (H100): 900 GB/s. Tiempo: 28/900 = 31 ms.
- PCIe 4.0 x16: 64 GB/s. Tiempo: 28/64 = 440 ms.
- InfiniBand HDR (200 Gb/s): 25 GB/s efectivos. Tiempo: 28/25 = 1,12 s.
- 10 GbE: 1,25 GB/s. Tiempo: 28/1,25 = 22 s. No hagas esto. (Pero es por lo que la gente paga InfiniBand.)
Si forward + backward tarda 200 ms en la GPU, entonces:
- NVLink: 31/200 = 15% de overhead de comunicación. Bien.
- PCIe: 440/200 = 220% — la comunicación domina al cómputo. Inútil.
- InfiniBand: 1120/200 = 560% — la comunicación domina al cómputo. También inútil sin optimizaciones.
Estos números explican por qué el entrenamiento de modelos grandes en producción usa NVLink intra-nodo, InfiniBand inter-nodo solo con solapamiento y bucketing de gradientes, y por qué el escalado DDP se rompe pasados los O(100) GPUs sin ZeRO o all-reduce jerárquico.
Para el grammar tutor con \(|\theta| = 500\)k:
- Buffer de gradientes: \(2 \cdot 500\text{k} = 1\) MB.
- Bytes por worker por all-reduce: 2 MB.
- En gloo sobre loopback (el setup del lab 01): despreciable. La comunicación es gratis a esta escala.
El sentido del lab 01 no es optimizar la comunicación. Es hacer que Borja lea la traza de torch.distributed y vea el all-reduce ocurrir.
Coste como $-por-step¶
Las matemáticas de ancho de banda de arriba se convierten en dólares multiplicando por la tasa dólar-por-segundo del cluster:
Para un cluster de 8 H100s en una gran nube a ~$30/hr por GPU (precio spot):
- $_por_segundo por GPU: $30/3600 = \(8,3 \times 10^{-3}\).
- 31 ms de comunicación × \(8,3 \times 10^{-3}\) × 8 GPUs = \(2,1 \times 10^{-3}\) por step en comunicación.
- A 1 step por segundo, día de entrenamiento = 86400 steps. Coste de comunicación ≈ $180/día.
Para el grammar tutor en el lab 02 (2× RTX 4090 a $0,70/hr total):
- $_por_segundo total: 0,70/3600 = \(1,9 \times 10^{-4}\).
- 1 ms (despreciable) de comunicación × \(1,9 \times 10^{-4}\) = \(1,9 \times 10^{-7}\) por step en comunicación.
- Lab de 3 horas = ~\(2 en cómputo, **\)\ll $0,01$ en comunicación**.
El coste de comunicación es una nota a pie al presupuesto del currículo. Pero la fórmula es la misma; solo cambian las constantes. El lab guía a Borja a través de su cálculo para el caso del grammar tutor para que exista la memoria muscular cuando el cluster crezca.
Cruce latencia vs ancho de banda¶
Para payloads muy pequeños (un solo bucket de gradientes de 1 KB), el all-reduce está dominado por latencia — los \(2(N-1)\) saltos de red dominan. El algoritmo adaptativo de NCCL usa tree all-reduce aquí.
Para payloads muy grandes (los 14 GB completos de gradientes), el all-reduce está dominado por ancho de banda — bytes por segundo dominan. Ring all-reduce gana.
El cruce está típicamente alrededor de 256 KB – 4 MB dependiendo del cluster. La variable de entorno NCCL_ALGO=tree|ring de NCCL fuerza una; en la práctica el auto-tuner acierta.
Para el grammar tutor con 2 MB totales de gradientes: justo en el cruce. Cualquier algoritmo funciona.
Lo que esta fase NO cubre¶
- Internos de NCCL: la implementación de los all-reduces ring/tree/double-binary-tree, la superficie GPU-aware MPI, el setup de GPUDirect RDMA. Territorio de despliegue de producción; no es pedagógicamente valioso a nuestra escala.
- Compresión de gradientes (1-bit Adam, PowerSGD, signSGD). Reduce el volumen de comunicación a costa de la convergencia — un área de investigación.
- All-reduce jerárquico (ring intra-nodo + tree inter-nodo). Estándar a gran escala, solo vocabulario.
- Benchmarking InfiniBand vs RoCE vs Slingshot. Fuera de presupuesto.
- Cómputo/red desagregados (CXL, fabrics futuros). Solo vocabulario.
Siguiente: theory/04-distributed-inference.md.