Tienes un modelo desplegado en un sistema. Arranca, los prompts reciben respuesta y aparece la pregunta difícil. ¿Esto es rápido?
El instinto lleva a mandar comandos curl, a escribir a mano un script de asyncio o a improvisar otro generador de carga de un solo uso. Todos esos caminos comparten el mismo problema, los límites de rendimiento de un solo proceso, el GIL de Python poniendo techo a la concurrencia o cifras medidas contra una referencia que construiste tú mismo. En cualquiera de los casos terminas con resultados en los que no puedes confiar del todo, pegados a una herramienta que vas a tener que reescribir apenas cambien los requisitos.
Lo que hace falta es un cliente de carga capaz de saturar un servidor real sin convertirse él mismo en el cuello de botella, que entregue resultados accionables y que tome cinco minutos de configuración en vez de cinco horas. Eso es NVIDIA AIPerf.
¿Qué hace distinto AIPerf?
AIPerf es el sucesor designado de GenAI-Perf y está reescrito desde cero. Las decisiones de diseño vienen de las lecciones de correr mediciones de modelos de lenguaje a escala.
- Corte limpio con la arquitectura anterior. AIPerf no corre encima de Perf Analyzer como hacía GenAI-Perf. Es una ruptura arquitectónica y es la razón por la que AIPerf escala como escala. Si estás portando un flujo de trabajo existente, la guía de migración cubre las diferencias principales.
- El cliente no debería ser el cuello de botella. La mayoría de los medidores, GenAI-Perf incluido, usa una arquitectura de proceso único que queda limitada por el GIL cuando hay concurrencia o tasa de peticiones reales. AIPerf es un sistema multiproceso. Los procesos de trabajo generan la carga, servicios separados de procesamiento de registros manejan los resultados y todo se coordina por ZMQ. Esa estructura permite medir el servidor con más exactitud, porque evita que AIPerf se transforme en el freno del lado del cliente.
- Amplitud de cargas de trabajo. AIPerf soporta más de 15 tipos de endpoint, entre ellos chat, respuestas, rankings de NIM y generación de imágenes, junto con conjuntos de datos públicos como ShareGPT y formatos de reproducción de trazas de Mooncake, Baseten y WEKA (AgentX). Sirve tanto para una prueba de humo sintética y rápida como para reproducir tráfico de producción capturado, sin cambiar de herramienta.
- Forma de la carga bajo control. AIPerf admite patrones de llegada constante, de Poisson y gamma, con ráfagas ajustables, rampas graduales de concurrencia y de tasa de peticiones, y distribuciones sintéticas que incluyen la razón de rango de vLLM y SGLang para ISL y OSL variables. Controlas la forma de la carga, no solamente su volumen.
La primera medición, ISL y OSL sintéticos sobre vLLM
Para este recorrido se usa Qwen3-0.6B servido a través de vLLM. La elección del modelo es deliberada, porque es chico como para correr en una sola GPU y rápido como para iterar sin esperas. El punto no es medir Qwen3-0.6B en particular, sino dejar armado el ciclo de medición. Una vez que lo tienes, cambiar a otro modelo o a otro endpoint es cosa de un flag.
Descarga y levanta vLLM con el parser de razonamiento activado.
docker pull vllm/vllm-openai:latest
docker run --gpus all -p 8000:8000 -e HF_TOKEN vllm/vllm-openai:latest \
--model Qwen/Qwen3-0.6B \
--reasoning-parser qwen3 \
--host 0.0.0.0 --port 8000Instalar AIPerf
Se puede usar uv para instalar una copia central.
uv tool install aiperfO dentro de un entorno virtual.
uv venv venv
source venv/bin/activate
uv pip install aiperfUna nota de plataforma. En aarch64 la dependencia crick viene solo como código fuente y necesita un toolchain de C (build-essential en Debian y Ubuntu, Development Tools en RHEL). Si la instalación se queda pegada en ese paquete, esa es la razón.
Correr la medición
Con el servidor arriba y AIPerf instalado, ya se puede correr el primer perfil.
aiperf profile \
--model Qwen/Qwen3-0.6B \
--endpoint-type chat \
--streaming \
--url localhost:8000 \
--synthetic-input-tokens-mean 128 \
--synthetic-input-tokens-stddev 0 \
--output-tokens-mean 128 \
--output-tokens-stddev 0 \
--extra-inputs min_tokens:128 \
--extra-inputs ignore_eos:trueAlgunos flags hacen más trabajo del que aparentan.
--synthetic-input-tokens-stddev 0 y --output-tokens-stddev 0 fijan la carga en exactamente 128 tokens de entrada y 128 de salida por petición. Eso reproduce una medición estática de uso común, que mantiene constantes el largo de la petición y el de la salida.
--extra-inputs min_tokens:128 y --extra-inputs ignore_eos:true le indican al modelo que emita de verdad los 128 tokens en vez de detenerse antes. Sin esos flags la cantidad de tokens de salida queda como una sugerencia. El modelo se detiene cuando termina de forma natural, que puede ser bastante antes del OSL buscado. Las cifras de throughput salen más bajas de lo que corresponde y no son reproducibles entre corridas.
--streaming no es opcional si quieres medir TTFT e ITL. Sin streaming el servidor agrupa la respuesta completa antes de enviarla, y no hay eventos de primer token ni de tokens de decodificación que medir.
Lo que vas a ver
La lectura de estas cifras viene en la sección siguiente. Por ahora conviene fijarse en la forma de la salida de la Figura 2, la latencia desglosada por percentil, el throughput en tokens por segundo y las estadísticas a nivel de petición, todo en un mismo lugar. Esa es la línea base contra la que vas a comparar todo lo demás.
¿Qué métricas entrega AIPerf?
Cuando una corrida termina, AIPerf imprime una tabla de métricas en la consola y escribe los resultados completos en CSV y JSON. Esto es lo que estás mirando.
- TTFT, tiempo hasta el primer token. Cuánto pasa desde que se envía la petición hasta que llega el primer token. Es la señal principal de latencia para los casos de uso interactivos.
- ITL, latencia entre tokens. El tiempo entre tokens sucesivos durante la generación. Un ITL alto significa que la fase de decodificación está sufriendo, aunque el TTFT se vea sano.
- Latencia de la petición. El tiempo de punta a punta de la respuesta completa. Combina el costo de prefill y el de decodificación en una sola cifra.
- Throughput de tokens de salida. Tokens generados por segundo sobre todas las peticiones concurrentes. Es la señal principal de throughput para planificar capacidad.
Las definiciones completas de estas y del resto de las métricas que reporta AIPerf están en la referencia de métricas.
Cada una de las métricas anteriores se reporta con desglose por percentiles (p25, p50, p75, p90, p95, p99) junto con mínimos, máximos, promedios y desviaciones estándar. Esos desgloses importan porque dejan a la vista las distribuciones de cola larga. Un servidor con un TTFT promedio sano y un p99 fuera de rango se ve bien en el agregado y falla en producción.
Con DCGM o pynvml disponibles, AIPerf también incorpora el consumo eléctrico de la GPU, su utilización y el uso de memoria dentro de la misma salida de la corrida. Correlacionar un peak de latencia con un evento de presión de memoria deja de requerir una sesión de perfilado aparte, porque la telemetría ya está ahí.
Configurar un patrón de tráfico
Con la medición estática andando, se puede explorar algo más dinámico. La sección anterior entregó un patrón de tráfico extremadamente fijo, y el tráfico de inferencia real no sigue un patrón estático. Para medir con un escenario menos rígido se pueden usar las perillas de carga sintética de AIPerf e introducir variabilidad en las peticiones.
aiperf profile \
--model Qwen/Qwen3-0.6B \
--endpoint-type chat \
--streaming \
--url localhost:8000 \
--request-rate 10 \
--arrival-pattern poisson \
--synthetic-input-tokens-mean 512 \
--synthetic-input-tokens-stddev 128 \
--output-tokens-mean 128 \
--output-tokens-stddev 32 \
--random-seed 42 \
--request-count 200Varias cosas cambiaron respecto de la medición estática.
--arrival-pattern poisson junto con --request-rate 10 significa que las peticiones llegan a un promedio de 10 por segundo, con tiempos entre llegadas sacados de una distribución exponencial. El servidor ahora experimenta ráfagas y huecos en vez de un flujo de un solo usuario, que es a lo que se parece una cola bajo tráfico real.
--synthetic-input-tokens-stddev 128 introduce varianza alrededor del promedio de 512 tokens y produce una mezcla de prompts cortos y largos. El servidor tiene que manejar largos de prompt variables durante el prefill en vez de prompts idénticos.
--output-tokens-stddev 32 agrega varianza del lado de la salida. Fíjate en que min_tokens e ignore_eos desaparecieron de este comando. En la medición estática esos flags clavaban la salida en exactamente 128 tokens para mantener limpia la línea base, y acá se libera esa restricción a propósito para que la distribución de salida pueda variar.
--random-seed 42 hace reproducibles los tiempos de Poisson y los sorteos de largo sintético. Volver a correr el mismo comando produce la misma secuencia de peticiones.
--streaming sigue sin ser opcional. Sin streaming el servidor agrupa la respuesta completa antes de enviarla y no hay eventos de primer token ni de decodificación que medir.
La diferencia que se ve en los resultados
Al mirar las métricas de esta corrida, las distribuciones salen bastante más anchas que en la línea base estática, que es lo esperable cuando hay más peticiones compitiendo al mismo tiempo por la GPU y los largos de prefill varían petición a petición.
En los gráficos de la Figura 4 se ve que la línea de comandos con Poisson introdujo una tasa de peticiones centrada alrededor de 10 por segundo, aunque sin coincidir exactamente. Esa tasa de llegada emula el jitter del momento en que llegan las peticiones, frente al modo constante que garantiza 10 peticiones por segundo fijas.
En la Figura 5 se aprecia la variación del largo de las peticiones alrededor del promedio de 512 tokens, con largos de secuencia de entrada que van de 154 a 818 tokens.
Al comparar el TTFT entre las dos corridas, la de Poisson muestra una dispersión mucho más ancha. Hay más peticiones compitiendo al mismo tiempo por la GPU, los largos de prefill varían y las operaciones de prefill y decodificación se superponen. El caso de concurrencia uno es un escenario idealizado que corre una petición a la vez y presenta el TTFT más bajo posible, a costa del throughput.
En esa Figura 6 se ve que la corrida de un solo usuario tiene menos variabilidad de TTFT que la carga mucho más variada del experimento con Poisson.










