Eficiencia de GPU: por qué «mejor» no siempre significa «más grande»

¿Te fue útil?

Alguien compra la brillante GPU de gama alta. El panel de control se enciende. La factura se enciende aún más. ¿Y el trabajo de entrenamiento? Funciona… básicamente a la misma velocidad que la máquina antigua.

Si alguna vez te has quedado mirando a nvidia-smi mostrando un 20–40% de utilización mientras tus stakeholders preguntan por qué su «actualización» no mejoró nada, estás en el lugar correcto. La cruda verdad: las GPUs no son aceleradores mágicos; son coprocesadores quisquillosos y hambrientos de ancho de banda. No las «usas». Las debes alimentar.

La idea central: más grande no es automáticamente mejor

«GPU mejor» suele significar más cálculo, más tensor cores, más ancho de banda de memoria, quizá más HBM y, a veces, más VRAM. Pero tu carga de trabajo es una línea de ensamblaje, no una sola máquina. Si la estación más lenta es el preprocesado en CPU, las lecturas de almacenamiento, las transferencias PCIe, el all-reduce o la sobrecarga de lanzamiento de kernels, entonces una GPU más grande simplemente se queda allí… esperando con educación.

En producción, la eficiencia de GPU no es «porcentaje de utilización». Es rendimiento del negocio por dólar por vatio por hora-hombre del ingeniero. Una GPU grande que está mayormente inactiva no es un producto premium; es un calefactor caro con excelente marketing.

Aquí está la declaración relevante para la toma de decisiones:

  • Si no puedes mantener ocupada una GPU de gama media, una GPU de gama alta no arreglará tu canalización.
  • Si tienes una carga estable y limitada por cálculo, una GPU más grande puede ser una victoria directa—pero solo si el interconectado, la memoria y la pila de software escalan con ella.
  • Si escalas a múltiples GPUs, la red y las operaciones colectivas se convierten en parte de tu «GPU», te guste o no.

Una cita que vale la pena pegar en el monitor: «La esperanza no es una estrategia.» — Gene Kranz

Sí, es cliché. También es lo que digo cuando alguien propone «compremos GPUs más grandes» sin medir a dónde va realmente el tiempo.

Un modelo mental de grado productivo sobre la eficiencia de GPU

1) Piensa en etapas, no en chips

Un bucle típico de entrenamiento/inferencia tiene etapas que pueden bloquearse independientemente:

  1. Fuente de datos: object storage, NFS, NVMe local, base de datos, feature store.
  2. Decodificar/augmentar: transformaciones en CPU, decodificación de imágenes, tokenización, compresión.
  3. Transferencia host→dispositivo: memoria pinneada vs pageable, PCIe vs NVLink, copias asíncronas.
  4. Cómputo en GPU: kernels, tensor cores, operaciones limitadas por memoria, reducciones.
  5. Dispositivo→dispositivo / colectivas: all-reduce multi-GPU, atención fragmentada, paralelismo por pipeline.
  6. Checkpointing/logging: latencia del sistema de archivos, tormentas de metadatos, escrituras síncronas.

La «GPU más grande» suele acelerar solo el paso 4. Si el paso 1 o 2 es el cuello de botella, el paso 4 no importa. Si el paso 5 domina a escala, el paso 4 se vuelve un error de redondeo.

2) Entiende lo que oculta la “utilización”

nvidia-smi GPU-Util es una señal gruesa: «¿estuvo la GPU haciendo algo recientemente?». No garantiza que estés ejecutando eficientemente. Una GPU puede mostrar 95% de utilización mientras ejecuta kernels limitados por memoria que apenas usan tensor cores, o mientras gira en kernels pequeños con gran sobrecarga de lanzamiento. Puede mostrar 30% de utilización mientras aún entrega un excelente rendimiento porque la carga es explosiva y está sobrepuesta.

3) Las GPUs más grandes tienen apetitos más grandes

Cuando saltas de una GPU más pequeña a una más grande, también aumentas la tasa mínima requerida para «alimentarla»:

  • Más cálculo: significa que necesitas batches mayores, más trabajo paralelo, kernels fusionados mejores o más concurrencia.
  • Más ancho de banda: exige patrones de acceso a memoria que lo aprovechen; el acceso aleatorio no se volverá secuencial mágicamente.
  • M más VRAM: puede reducir transferencias CPU‑GPU y permitir modelos/batches más grandes—pero solo si tu framework lo usa de forma inteligente.

4) La latencia y la sobrecarga no disminuyen

La sobrecarga de lanzamiento de kernels, la sobrecarga del intérprete de Python, la sincronización por paso, el logging y la coordinación del dataloader pueden dominar en modelos pequeños o con batches pequeños. Una GPU más rápida acorta el tiempo de cálculo, haciendo que la sobrecarga sea una fracción mayor del total. Felicidades: te «optimizaste» hasta crear un cuello de botella.

Chiste #1 (corto y relevante): Una GPU más grande no puede arreglar un dataloader lento más de lo que una taza de café más grande arregla el insomnio.

5) La eficiencia es un contrato de pila

En términos SRE, tu GPU está aguas abajo de: almacenamiento, programación de CPU, comportamiento del asignador de memoria, runtime de contenedores, controladores, librerías y, a veces, un scheduler de clúster con opiniones. Si alguna capa rompe el contrato—I/O lento, vecinos ruidosos, pinning NUMA incorrecto, versión CUDA equivocada—no estás «ligado a la GPU», estás «ligado a todo lo demás».

Hechos interesantes y contexto histórico (que realmente importan)

Esto no es trivia para la noche del bar. Cada punto se corresponde con un modo real de fallo o decisión de diseño.

  1. Las GPUs se volvieron de propósito general por accidente y perseverancia: los primeros trabajos de «GPGPU» usaban APIs de gráficos para cómputo antes de que CUDA lo hiciera mainstream. El legado explica por qué los patrones de acceso a memoria siguen importando tanto.
  2. Apuesta de CUDA de la era 2007: el modelo de programación de NVIDIA hizo accesible el cómputo GPU, pero también creó un ecosistema donde desajustes entre driver/toolkit pueden costar rendimiento silenciosamente.
  3. PCIe ha sido un limitador recurrente: para muchas cargas, los cuellos de botella en transferencias host‑dispositivo persisten incluso cuando el cómputo GPU avanza a saltos.
  4. HBM cambió el juego para modelos que usan mucho ancho de banda: la memoria de alto ancho de banda ayuda, pero los kernels limitados por memoria aún requieren localidad y coalescencia; el ancho de banda no arregla patrones de acceso pobres.
  5. Los tensor cores cambiaron la conversación sobre el «dtype óptimo»: la precisión mixta puede ser transformadora, pero no es gratis—el scaling de pérdida, la numérica y la sobrecarga de conversión pueden morderte.
  6. NVLink/NVSwitch surgieron porque PCIe no era suficiente: el escalado multi‑GPU a menudo depende más del interconectado y la topología que de los FLOPS brutos.
  7. El deep learning popularizó los «input pipelines» como trabajo de rendimiento de primera clase: la gente aprendió a la fuerza que la decodificación JPEG puede ser tu «cuello de botella GPU». No lo es.
  8. El checkpointing se volvió más difícil a medida que los modelos crecieron: guardar estado puede convertirse en un problema de almacenamiento distribuido y metadatos, no en «escribir un archivo».

Guía rápida de diagnóstico (primeras/segundas/terceras comprobaciones)

Primero: ¿La GPU está realmente hambrienta?

  • Mira la utilización y el uso de memoria por proceso.
  • Comprueba si el consumo de energía de la GPU está cerca del esperado en estado estable.
  • Comprueba si la CPU está saturada o el iowait es alto.

Si la util GPU es baja y la CPU/iowait es alta, deja de culpar a la GPU. Arregla al alimentador.

Segundo: ¿Está acotada por cómputo, memoria o por lanzamiento/sobre‑carga?

  • Compara TFLOPS conseguidos con los teóricos (aproximadamente) usando métricas del profiler.
  • Revisa el throughput de memoria GPU e indicadores de ocupación de SM.
  • Busca muchos kernels pequeños, puntos de sincronización o sobrecarga de Python.

Si estás limitado por memoria, «más cómputo» no ayudará mucho. Si estás limitado por sobrecarga, una GPU más rápida lo empeora.

Tercero: Si es multi‑GPU, ¿es el interconectado el verdadero cuello de botella?

  • Comprueba la topología: presencia de NVLink, generación PCIe, colocación NUMA.
  • Revisa el tiempo de all‑reduce de NCCL en tus trazas de profiler.
  • Comprueba la saturación de red (para multi‑nodo): RDMA, TCP, contadores del switch.

Si las colectivas dominan, escalar a una GPU más grande por nodo puede ser mejor que escalar a muchos nodos—o al revés—dependiendo de la topología. Mide, no vibes.

Tareas prácticas: comandos, salidas y decisiones

Estos son los chequeos que puedes ejecutar durante un puente de incidente sin necesitar una configuración de profiler a medida. Cada tarea incluye: comando, salida típica, qué significa y la decisión que tomas.

Task 1: Confirma visibilidad GPU, salud del driver y carga básica

cr0x@server:~$ nvidia-smi
Wed Jan 21 12:11:03 2026
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15    Driver Version: 550.54.15    CUDA Version: 12.4               |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  NVIDIA A100-PCIE-40GB          On  | 00000000:65:00.0 Off |                    0 |
|  33%   62C    P0              185W / 250W |  12450MiB / 40536MiB |     38%      Default |
+-----------------------------------------+----------------------+----------------------+
| Processes:                                                                              |
|  GPU   GI   CI        PID   Type   Process name                              GPU Memory |
|=======================================================================================|
|    0   N/A  N/A     21983      C   python3                                     12340MiB |
+---------------------------------------------------------------------------------------+

Qué significa: versiones de driver/toolkit, consumo de energía, memoria en uso, util y qué proceso la posee.

Decisión: Si GPU-Util es baja y la energía es baja en estado «estable», probablemente estás hambriento o limitado por sobrecarga. Sube por la pila.

Task 2: Vigilar utilización y potencia en el tiempo (detectar starvation)

cr0x@server:~$ nvidia-smi dmon -s pucvmt
# gpu   pwr gtemp mtemp    sm   mem   enc   dec  mclk  pclk
# Idx     W     C     C     %     %     %     %   MHz   MHz
    0   92    56     -    12     8     0     0  1215  1410
    0  210    64     -    85    72     0     0  1215  1410
    0   75    55     -     9     6     0     0  1215  1410

Qué significa: picos de alto SM% seguidos por valles inactivos suelen indicar paradas en el pipeline de entrada o puntos de sincronización.

Decisión: Si ves un patrón «dientes de sierra», investiga el dataloader, copias host‑device y la contención de CPU.

Task 3: Identificar saturación CPU vs iowait (falla clásica del alimentador)

cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.5.0 (server) 	01/21/2026 	_x86_64_	(64 CPU)

12:12:11 PM  CPU   %usr %nice  %sys %iowait  %irq %soft  %steal  %idle
12:12:12 PM  all   420.0  0.0   18.0   60.0   0.0  2.0    0.0   0.0
12:12:12 PM   12    97.0  0.0    1.0    2.0   0.0  0.0    0.0   0.0
12:12:12 PM   13    95.0  0.0    3.0    2.0   0.0  0.0    0.0   0.0

Qué significa: %usr alto en muchos núcleos sugiere preprocesado en CPU o sobrecarga de Python; %iowait alto sugiere paradas de almacenamiento.

Decisión: Si %iowait es alto, perfila almacenamiento. Si %usr está al máximo, optimiza decodificación/tokenización, aumenta workers o descarga transformaciones.

Task 4: Comprobar I/O por proceso (¿tu dataset es lento?)

cr0x@server:~$ iostat -xz 1 2
Linux 6.5.0 (server) 	01/21/2026 	_x86_64_	(64 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          30.12    0.00    2.10   22.45    0.00   45.33

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s  w_await aqu-sz  %util
nvme0n1         420.0   58240.0     0.0   0.00    8.10   138.67    15.0   2048.0    2.30   3.60  82.00

Qué significa: %util alto y r_await alto indican que el dispositivo está ocupado y las lecturas esperan.

Decisión: Si el almacenamiento está caliente, cachea datasets localmente, aumenta el paralelismo de lectura con cuidado o cambia el formato (menos archivos pequeños).

Task 5: Detectar “demasiados archivos pequeños” (dolor de metadatos)

cr0x@server:~$ find /datasets/vision/train -type f | head -n 5
/datasets/vision/train/000001.jpg
/datasets/vision/train/000002.jpg
/datasets/vision/train/000003.jpg
/datasets/vision/train/000004.jpg
/datasets/vision/train/000005.jpg

Qué significa: datasets con una imagen por archivo pueden aplastar el rendimiento de metadatos en sistemas de archivos en red.

Decisión: Consolida en formatos de shards (tar/LMDB/record files), o estagea localmente en NVMe.

Task 6: Comprobar velocidad y ancho de enlace PCIe (asesino silencioso de throughput)

cr0x@server:~$ nvidia-smi -q -d pcie | sed -n '1,80p'
==============NVSMI LOG==============

GPU 00000000:65:00.0
    PCIe Generation
        Current                       : 3
        Max                           : 4
    Link Width
        Current                       : x8
        Max                           : x16
    Tx Throughput                     : 1200 KB/s
    Rx Throughput                     : 980 KB/s

Qué significa: funcionar en Gen3 x8 cuando esperabas Gen4 x16 es una reducción real en el ancho de banda host‑dispositivo.

Decisión: Reasienta la tarjeta, revisa la configuración del BIOS, verifica risers, confirma el cableado del slot y que no compartas lanes con otros dispositivos.

Task 7: Validar localidad NUMA (la CPU alimenta la GPU desde el socket correcto)

cr0x@server:~$ nvidia-smi topo -m
        GPU0    CPU Affinity    NUMA Affinity
GPU0     X      0-31            0

Legend:
  X    = Self

Qué significa: los núcleos CPU 0–31 son locales a GPU0. Usar el socket equivocado puede aumentar la latencia y reducir el ancho de banda efectivo.

Decisión: Fija los workers del dataloader y el proceso principal al nodo NUMA local cuando el rendimiento importe.

Task 8: Comprobar thermal o power throttling (el freno de mano invisible)

cr0x@server:~$ nvidia-smi -q -d PERFORMANCE | sed -n '1,120p'
==============NVSMI LOG==============

GPU 00000000:65:00.0
    Performance State               : P2
    Clocks Throttle Reasons
        Idle                        : Not Active
        Applications Clocks Setting  : Not Active
        SW Power Cap                : Active
        HW Slowdown                 : Not Active

Qué significa: SW Power Cap: Active sugiere que tu trabajo alcanza un límite de energía (o un cap configurado) y reduce relojes.

Decisión: Ajusta el límite de potencia (si la política lo permite), mejora la refrigeración o acepta que tu GPU «más grande» no correrá a los relojes de portada.

Task 9: Confirmar que la frecuencia CPU no esté atascada en bajo (mala config del host del contenedor)

cr0x@server:~$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
powersave

Qué significa: CPUs en powersave pueden ralentizar el preprocesado y la coordinación, dejando a las GPUs sin alimento.

Decisión: Establece el governor en performance en nodos dedicados a entrenamiento (según la política de operaciones).

Task 10: Confirmar síntomas de presión de hugepages / memoria pinneada (bloqueos H2D)

cr0x@server:~$ grep -E 'HugePages|MemAvailable' /proc/meminfo
MemAvailable:   18432000 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0

Qué significa: no es definitivo, pero poca memoria disponible combinada con gran carga de dataloading puede aumentar el paging y ralentizar las transferencias.

Decisión: Reduce la presión de memoria del host, considera ajustes de memoria pinneada y evita oversubscription de RAM con demasiados workers.

Task 11: Comprobar límites cgroup CPU del contenedor (hambre autoimpuesta)

cr0x@server:~$ cat /sys/fs/cgroup/cpu.max
200000 100000

Qué significa: cuota 200ms por periodo de 100ms efectivamente limita a 2 CPUs de tiempo. Para un trabajo GPU, eso suele ser ridículo.

Decisión: Aumenta las solicitudes/límites de CPU para que tu pipeline de entrada pueda respirar; empareja CPU con la clase de GPU.

Task 12: Comprobar starvation de workers del dataloader (síntomas en el lado de Python)

cr0x@server:~$ ps -o pid,pcpu,pmem,cmd -C python3 --sort=-pcpu | head
  PID %CPU %MEM CMD
21983 780.2 12.4 python3 train.py --config prod.yaml
22010  95.1  1.2 python3 -c from multiprocessing.spawn import spawn_main; spawn_main(...)
22011  94.6  1.2 python3 -c from multiprocessing.spawn import spawn_main; spawn_main(...)

Qué significa: varios procesos worker están activos; si solo el proceso principal está ocupado, tus workers pueden estar atascados en I/O, trabajo pesado con el GIL o mal configurados.

Decisión: Ajusta el número de workers, mueve transformaciones a código nativo, precomputar features o usa formatos de dataset más rápidos.

Task 13: Observar throughput de red para entrenamiento multi‑nodo

cr0x@server:~$ sar -n DEV 1 2 | grep -E 'IFACE|mlx|eth'
12:15:01 PM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s
12:15:02 PM       eth0   8200.00   7900.00  910000.00  880000.00
12:15:03 PM       eth0   8300.00   8100.00  920000.00  895000.00

Qué significa: si la red está saturada durante fases de all‑reduce, el escalado se detiene.

Decisión: Si estás saturando, revisa ajustes NCCL, topología, compresión de gradientes o el conteo de nodos.

Task 14: Identificar picos de latencia del sistema de archivos durante checkpointing

cr0x@server:~$ dmesg | tail -n 8
[123456.120001] nfs: server nfs01 not responding, still trying
[123456.421019] nfs: server nfs01 OK
[123457.002341] INFO: task python3:21983 blocked for more than 120 seconds.

Qué significa: tu paso de entrenamiento puede estar bien; la escritura del checkpoint pausa todo.

Decisión: Haz el checkpointing asíncrono, escribe localmente y luego sube, o cambia el backend de almacenamiento.

Dónde fallan las GPUs grandes: cuellos de botella por capa

Pipeline de entrada: el impuesto silencioso al throughput

Si entrenas con imágenes, video o grandes corpus de texto, estás manejando una fábrica:

  • Leer bytes comprimidos
  • Decodificar (a menudo en CPU)
  • Transformar/augmentar
  • Batch, collation, padding
  • Copiar a GPU

Cada etapa puede ser «lo suficientemente rápida» para una GPU más pequeña y de repente insuficiente para una más grande. La actualización no rompe el código; rompe las suposiciones sobre cuánto margen tenías.

Las GPUs de gama alta amplifican pipelines débiles. Eso no es una metáfora; es teoría básica de colas. Cuando el tiempo de servicio en una etapa baja, la siguiente etapa se convierte en el cuello de botella.

Sobrecarga del lado CPU: muerte por mil sincronizaciones

Python es maravilloso para expresar intención. No lo es para ejecutar operaciones pequeñas y frecuentes en un bucle cerrado mientras coordina un dispositivo capaz de procesar teraflops. Si tu bucle por paso incluye logging, agregación de métricas, sincronizaciones frecuentes con el dispositivo o trabajo en CPU no vectorizado, una GPU más rápida acorta la ventana de cómputo y empeora proporcionalmente la sobrecarga CPU.

Culpables comunes:

  • Llamar a .item() o forzar sincronización cada paso
  • Cambios frecuentes de forma que impiden la fusión de kernels
  • Tamaños de batch demasiado pequeños (especialmente en inferencia)
  • Preprocesado excesivo por muestra en Python

Transferencia host‑dispositivo: PCIe no es una sugerencia

Cuando la gente habla de «velocidad GPU», a menudo olvida el bus. El ancho de banda y la latencia de PCIe son propiedades fijas de tu plataforma, no de tus aspiraciones. Si tu modelo mueve datos repetidamente entre CPU y GPU, o si tienes copias de memoria pageable y puntos de sincronización, puedes quedar limitado por transferencias mucho antes del cómputo.

Patrón anti‑típico: «Mantendremos embeddings en CPU para ahorrar VRAM.» En una GPU grande, eso puede ser catastrófico.

Kernels limitados por memoria: cuando los FLOPS no importan

Algunas operaciones están limitadas por ancho de banda de memoria o comportamiento de caché, no por cómputo. Piensa en ops elementales, ciertas capas de normalización, búsquedas de embedding y patrones de atención que no están optimizados para localidad. Puedes comprar más cómputo y ver poca mejora.

Señales:

  • GPU‑Util alto pero consumo de energía modesto respecto a lo esperado
  • El profiler muestra baja utilización de tensor cores
  • Throughput de memoria cerca del pico mientras la utilización de SM está «ocupada» pero no productiva

Multi‑GPU: los impuestos del escalado llegan rápido y no se van

El entrenamiento multi‑GPU es una discusión con la física. En algún punto, pasas más tiempo coordinando que calculando. Las GPUs más grandes pueden ayudar al reducir el número de GPUs necesarias para un batch/modelo dado, lo que reduce la sobrecarga colectiva. O pueden perjudicar al incentivar un mayor conteo de nodos con un interconectado débil, haciendo que el all‑reduce domine.

La topología importa. NVLink vs PCIe. Colocación en sockets. Oversubscription del switch. Si no conoces tu topología, no conoces tu rendimiento.

Checkpointing y observabilidad: el fenómeno «iba bien hasta que guardó»

El checkpointing suele ser síncrono. Si escribes desde cada rank, o escribes en un filesystem compartido lento, tu trabajo parecerá «misteriosamente lento» aun cuando el cómputo esté sano. Los picos de latencia del almacenamiento se convierten en picos de inactividad de la GPU.

Chiste #2 (corto y relevante): Hacer checkpoint a un filesystem compartido en horas pico es una gran manera de aprender qué se siente la «retropresión», emocionalmente.

Tres mini-historias del mundo corporativo (anonimizadas)

Mini-historia 1: El incidente causado por una suposición equivocada

Una compañía desplegó nuevos nodos GPU para acelerar el retraining de un modelo de recomendación. La petición de cambio fue limpia: GPUs más grandes, mismo código, mismo dataset. La suposición era simple y reconfortante: «El cómputo era el cuello de botella.»

En días, el SLA de entrenamiento se deterioró. Las GPUs reportaban baja utilización y el canal on‑call se llenó de capturas de aceleradores inactivos y facturas de nube muy activas. Alguien culpó a los drivers. Otro culpó al framework. Lo habitual.

El problema real fue más aburrido: el dataset vivía en un sistema de archivos en red compartido. Los nodos antiguos tenían GPUs más pequeñas que tardaban más por paso, así que la latencia del filesystem quedaba oculta bajo el tiempo de cómputo. Las GPUs nuevas acortaron tanto el cómputo que cada paso topaba con una pared de lectura/metadatos. El trabajo no se aceleró; se volvió más sensible.

La solución fue estagear datos: rsync nocturno (o sincronización desde object‑store) a NVMe local, además de shardear archivos pequeños en blobs mayores. La utilización subió, el tiempo de entrenamiento bajó y el incidente se detuvo. La lección no fue «los filesystems en red son malos». La lección fue: mide la canalización de extremo a extremo antes de cambiar solo una estación.

Mini-historia 2: La optimización que salió mal

Otro equipo optimizó el uso de memoria GPU reduciendo agresivamente el tamaño de batch y habilitando gradient checkpointing en todas partes. El uso de VRAM se veía genial. El modelo entraba cómodamente, dejando espacio para crecimiento futuro. Todos celebraron—en silencio, porque celebrar también es sobrecarga.

Entonces el throughput se desplomó. Las GPUs estaban «ocupadas», pero el tiempo por paso aumentó. El profiler mostró más recomputación (esperado) y una avalancha de kernels pequeños (menos esperado). El batch más pequeño aumentó la sobrecarga por paso, redujo las oportunidades de fusión de kernels y empeoró la eficiencia del all‑reduce porque la relación cómputo/comunicación cayó.

Habían optimizado la restricción equivocada. El sistema no estaba limitado por VRAM; estaba limitado por latencia/sobre‑carga. La «optimización de memoria» se pagó con cómputo, sincronización y comunicación.

El plan de recuperación fue pragmático: aumentar el tamaño de batch hasta que los kernels volvieran a ser gruesos, hacer checkpoint solo en las capas que importaban y usar precisión mixta con validación cuidadosa. El uso de memoria subió. El throughput subió más. La métrica del negocio—modelos entrenados por día—finalmente coincidió con el gasto en hardware.

Mini-historia 3: La práctica aburrida pero correcta que salvó el día

Un equipo de plataforma tenía una regla que molestaba a todos: cada imagen de nodo GPU tenía una pequeña «suite de sanity de rendimiento» que corría tras el aprovisionamiento. Comprobaba ancho de enlace PCIe, governor CPU, compatibilidad driver/librería y pruebas básicas de ancho de banda NCCL. La gente lo llamaba burocracia.

Una semana, llegó un lote de servidores nuevos. Los trabajos corrían, pero el rendimiento multi‑GPU era pésimo en un subconjunto de nodos. La suite marcó inmediatamente esos nodos: las GPUs negociaron un ancho de enlace PCIe menor por un problema en los risers, y algunos nodos tenían un ajuste BIOS que limitaba la velocidad del enlace. Las GPUs estaban bien. La plataforma no.

Puesto que las comprobaciones corrían automáticamente, el equipo puso en cuarentena los nodos malos antes de que contaminaran la flota de entrenamiento. No hubo incidente prolongado, ni caza de brujas, ni un postmortem con el culpable equivocado.

Fue aburrido. Fue correcto. Y ahorró días de tiempo de ingeniero—que es el recurso más caro del edificio.

Errores comunes: síntoma → causa raíz → solución

1) Baja GPU‑Util, alto uso de CPU

Síntoma: la GPU se queda en 10–40%, CPU saturada.

Causa raíz: preprocesado en CPU (decodificación/tokenización/augmentación), sobrecarga de Python, pocos workers del dataloader.

Solución: aumenta workers, mueve transformaciones a operaciones vectorizadas/nativas, cachea datos preprocesados, fija al nodo NUMA correcto, asegúrate de que los límites de CPU del contenedor no te estrangulen.

2) Baja GPU‑Util, alto iowait

Síntoma: valles de inactividad de la GPU coinciden con picos de latencia de almacenamiento.

Causa raíz: dataset en almacenamiento de red lento, demasiados archivos pequeños, contención de metadatos.

Solución: shardear datasets, estagear a NVMe local, usar read‑ahead/caching, evitar aperturas de archivo por muestra, hacer checkpointing asíncrono.

3) Alta GPU‑Util pero throughput decepcionante

Síntoma: 90–100% de util, pero tiempo por paso no es bueno.

Causa raíz: kernels limitados por memoria, mala fusión de kernels, atención no optimizada, conversiones de dtype.

Solución: usa kernels fusionados cuando sea posible, ajusta batch/longitud de secuencia para mejorar eficiencia, valida precisión mixta, perfila para encontrar las ops calientes.

4) El rendimiento empeora tras actualizar a una GPU «más grande»

Síntoma: la GPU nueva es más lenta o solo marginalmente más rápida.

Causa raíz: tamaño de batch sin cambio que lleva a que la sobrecarga domine; PCIe negociado a menor velocidad/anchura; throttling por potencia; governor CPU incorrecto.

Solución: re‑ajusta el batch, valida Gen/anchura PCIe, revisa razones de throttle, ajusta el governor CPU apropiadamente, verifica localidad NUMA.

5) El escalado multi‑GPU se estanca tras 2–4 GPUs

Síntoma: duplicar GPUs no duplica throughput.

Causa raíz: all‑reduce domina, interconectado débil, topología mala, batch por GPU pequeño.

Solución: aumenta trabajo por GPU, optimiza colectivas (tamaños de bucket), usa menos nodos con GPUs más grandes, o mejora la alineación de red/interconectado.

6) Lentitud aleatoria o «cada 10 minutos se pausa»

Síntoma: entrenamiento estable interrumpido por paradas periódicas.

Causa raíz: checkpointing/logging, pausas de GC, problemas del filesystem, compactaciones en background.

Solución: escalona checkpoints, escribe localmente y luego sube, reduce logging síncrono, monitoriza latencia del filesystem, evita hotspots de metadatos compartidos.

Listas de comprobación / plan paso a paso

Paso a paso: hacer que una GPU más grande sea realmente más rápida

  1. Establece una línea base: mide imágenes/s, tokens/s o requests/s en la GPU antigua con el mismo código y snapshot del dataset.
  2. Confirma sanidad de la plataforma: versión de driver, Gen/anchura PCIe, governor CPU, topología NUMA, límites de potencia.
  3. Revisa al alimentador: throughput/latencia de almacenamiento, salud de workers del dataloader, margen de CPU, presión de memoria.
  4. Reajusta tamaño de batch: la GPU más grande suele querer batches mayores; si no puedes, espera retornos decrecientes.
  5. Perfila una corrida representativa: identifica kernels principales, tiempo de stall CPU, tiempo H2D, tiempo de colectivas.
  6. Arregla la mayor parada: no «optimices todo». Elige el cuello de botella que domina el tiempo de pared.
  7. Valida la corrección: especialmente con precisión mixta y kernels fusionados—las regresiones silenciosas de exactitud son outages reales, solo más lentos.
  8. Revisa la eficiencia por dólar: a veces dos GPUs «más pequeñas» bien alimentadas superan a una gigante con un pipeline hambriento.

Checklist: qué evitar al comprar GPUs

  • Comprar por TFLOPS pico sin mapear el cuello de botella de tu carga (cómputo vs memoria vs I/O vs comunicación).
  • Asumir que la VRAM lo soluciona todo; soluciona algunas cosas y crea otras (checkpoints más grandes, arranque más largo, fallos más costosos).
  • Ignorar la topología de interconexión para multi‑GPU (líneas PCIe, presencia de NVLink, colocación NUMA).
  • Ignorar la CPU: una CPU débil puede dejar hambrienta a una GPU monstruosa.
  • Ignorar el almacenamiento: «el dataset está en NFS compartido» no es un plan de rendimiento.

Checklist: controles SRE aburridos que mantienen rápida la flota GPU

  • Pruebas de aceptación a nivel de nodo: Gen/anchura PCIe, pruebas básicas de ancho de banda, comprobaciones de razones de throttle.
  • Imágenes estandarizadas con compatibilidad fija de driver/librerías.
  • Dashboards que muestren util GPU, potencia, BW de memoria, iowait CPU y latencia de almacenamiento.
  • Automatización de cuarentena para nodos que negocien ancho de enlace inferior o muestren errores Xid repetidos.
  • Runbooks específicos por carga (training vs inference vs jobs batch de embeddings).

Preguntas frecuentes

1) Si mi utilización GPU es baja, ¿la GPU es el problema?

Normalmente no. La baja utilización suele ser síntoma de hambre: preprocesado en CPU, almacenamiento, red o sincronización. Confirma con consumo de energía y CPU/iowait.

2) ¿Por qué al actualizar a una GPU más rápida mi trabajo empeoró?

Acortaste el segmento de cómputo, así que la sobrecarga fija (Python, coordinación del dataloader, I/O) se volvió dominante. Las GPUs más grandes reducen el tiempo de cómputo; no reducen tus malos hábitos.

3) ¿Aumentar el tamaño de batch siempre es la respuesta?

No. Puede mejorar la eficiencia de kernels y amortizar la sobrecarga, pero puede dañar la convergencia, aumentar la memoria o desplazar cuellos de botella a la comunicación. Ajusta con métricas, no por fe.

4) PCIe vs NVLink: ¿cuándo debería importarme?

Importa cuando haces trabajo multi‑GPU en un nodo o transferencias host‑device frecuentes. Si las colectivas o las transferencias peer‑to‑peer son significativas, la topología importa para el rendimiento.

5) La precisión mixta no aceleró. ¿Por qué?

Puedes estar limitado por memoria, por sobrecarga o gastando tiempo en conversiones de dtype. O tu modelo tiene ops que no se benefician de tensor cores. Perfila para verificar a dónde va el tiempo.

6) ¿Cuál es la forma más rápida de saber si estoy limitado por almacenamiento?

Mira iowait alto y alta utilización de disco/red durante el entrenamiento, más patrones de SM% en dientes de sierra en la GPU. Luego valida con iostat y logs del filesystem.

7) ¿Por qué el multi‑GPU no escala linealmente?

Porque pagas costos de coordinación (all‑reduce, sincronización, stragglers). Al añadir GPUs, la comunicación crece y eventualmente domina a menos que el trabajo por GPU también crezca.

8) ¿Debería comprar una GPU enorme o varias pequeñas?

Depende de tu cuello de botella. Si estás limitado por comunicación, menos GPUs más grandes pueden ser mejores. Si necesitas throughput para muchos jobs independientes, múltiples GPUs pequeñas suelen ganar operativamente.

9) ¿Puede el scheduling de Kubernetes afectar la eficiencia GPU?

Sí. Límites de CPU, presión de memoria, vecinos ruidosos y mala alineación NUMA pueden dejar hambrientas a las GPUs. Si tratas pods GPU como pods web sin estado, aprenderás nuevas formas de tristeza.

10) ¿Cuál es un objetivo «bueno» de utilización GPU?

No hay un número único. Para entrenamiento estable, 80–95% puede estar bien. Para inferencia explosiva, una utilización menor puede ser óptima si se cumplen los SLO de latencia. Monitorea throughput y latencia tail, no métricas de ego.

Pasos prácticos siguientes

Si estás a punto de comprar GPUs más grandes, o ya lo hiciste y te arrepientes, haz esto en orden:

  1. Ejecuta la guía rápida de diagnóstico y captura una ventana de 10 minutos de util GPU, potencia, uso CPU, iowait y estadísticas de almacenamiento/red.
  2. Valida lo básico de la plataforma: Gen/anchura PCIe, razones de throttle, afinidad NUMA, governor CPU, límites de CPU del contenedor.
  3. Arregla al alimentador antes de tocar el código del modelo: shardea datos, estagea localmente, reduce overhead de archivos pequeños, añade paralelismo donde realmente sea útil.
  4. Luego ajusta la carga: tamaño de batch, precisión mixta, oportunidades de fusión de kernels y ajustes de comunicación multi‑GPU.
  5. Institucionaliza las comprobaciones aburridas: pruebas de aceptación en nodos, dashboards que muestren starvation y un runbook que nombre los principales cuellos de botella que realmente has visto.

El remate no es «no compres GPUs grandes». Cómpralas cuando la carga lo merezca. Pero trata el rendimiento GPU como cualquier otro sistema en producción: mide el cuello de botella, cambia una cosa, verifica, repite. El hardware es rápido. Los sistemas son lentos. Por eso tienes trabajo.

← Anterior
Planificación de capacidad ZFS: diseñar para el crecimiento sin reconstruir
Siguiente →
Latencias RAM sin dolor: MHz vs CL y qué comprar

Deja un comentario