Curso exprés · No. 24
El trabajo de rendimiento falla siempre de la misma manera: alguien adivina qué es lento, optimiza lo que no toca y añade complejidad para nada. La disciplina es lo contrario — mide para encontrar el bottleneck real, arregla esa única cosa y vuelve a medir. Aprende qué hace realmente lento al software, y las palabras para nombrarlo, y «hazlo más rápido» se convierte en un método en lugar de una corazonada.
Solo lo esencial · Una imagen por idea · Aprende las palabras
La primera regla del rendimiento es la que todo el mundo rompe: no te fíes de tu corazonada sobre qué es lento. Casi toda la optimización malgastada viene de saltarse este único paso.
Tu intuición sobre la lentitud suele equivocarse
Un médico que prescribe cirugía basándose en una corazonada, sin haber hecho nunca una prueba — seguro, rápido y, con demasiada frecuencia, operando algo que no era el problema.
Los desarrolladores son famosos por adivinar mal dónde se va el tiempo. La parte que crees lenta a menudo está bien; el verdadero culpable está en un sitio que nunca sospechaste — una función diminuta llamada un millón de veces, una llamada oculta a la base de datos. Actuar por corazonada significa que optimizas lo que no toca, añades complejidad y el programa sigue siendo lento. El primer movimiento nunca es «arréglalo» — es averiguar adónde se va realmente el tiempo.
Usa el profiler para encontrar el coste real
Una factura detallada que muestra exactamente adónde se fue el dinero, línea por línea — para que dejes de adivinar y veas que fue ese cargo, no los que suponías, el que se comió el presupuesto.
Un profiler es una herramienta que mide dónde gasta realmente el tiempo tu programa, función por función. Convierte «creo que esto es lento» en «esta función es el 80% del tiempo de ejecución». Con eso, arreglas lo que importa en lugar de lo que imaginabas. Ya sea un profiler, logs de tiempos o tus métricas de observabilidad, el principio se mantiene: deja que la medición, no la intuición, te señale el problema.
Mide, arregla, vuelve a medir
Un científico cambia una variable, repite el experimento y comprueba el resultado — nunca da por hecho que un cambio ayudó, siempre confirma que así fue.
El rendimiento es un bucle: mide para encontrar la parte lenta, cámbiala y vuelve a medir para confirmar que el cambio ayudó de verdad — y que no se limitó a mover el problema o a empeorarlo. Sin la segunda medición estás adivinando si mejoraste algo, que es como las «optimizaciones» empeoran las cosas en silencio. Trata cada arreglo como una hipótesis que verificas, la misma disciplina que las evals para la IA o los tests para el código.
La intuición sobre la lentitud suele equivocarse. Usa el profiler para encontrar adónde se va realmente el tiempo, arréglalo y vuelve a medir para confirmar — nunca optimices por una corazonada.
Antes de optimizar, sabe qué tipo de «rápido» quieres en realidad. Dos objetivos distintos se confunden constantemente, y mejorar uno puede no hacer nada por el otro.
La latency es la espera por una sola cosa
Cuánto tarda un solo café desde el pedido hasta la taza — la experiencia de un cliente, medida de principio a fin.
La latency es cuánto tarda una operación de principio a fin — una petición, una carga de página, una consulta. Es lo que un usuario individual siente: la espera. Cuando alguien dice «el sitio es lento», casi siempre se refiere a la latency — el retraso entre hacer algo y obtener una respuesta. Bajar la latency es hacer que cada cosa individual ocurra más rápido.
El throughput es cuánto manejas por segundo
Cuántos cafés sirve la cafetería entera en una hora — no cuánto tarda uno, sino el flujo total a través del local.
El throughput es cuánto trabajo maneja el sistema por unidad de tiempo — peticiones por segundo, trabajos por minuto. Tiene que ver con la capacidad total, no con la espera individual. Un sistema puede tener un gran throughput (sirviendo a miles a la vez) mientras cada usuario aún espera un rato (latency alta), o latency baja pero capacidad limitada. Son objetivos distintos, y cuál persigues cambia lo que arreglas.
Son distintos, y a veces se contraponen
Una moto lleva a una persona allí más rápido (latency baja); un autobús mueve a más gente por viaje (throughput alto). El mejor vehículo depende de cuál necesites.
La latency y el throughput son independientes, y optimizar uno puede perjudicar al otro. El batching — agrupar trabajo — a menudo sube el throughput pero añade latency, porque cada elemento espera al lote. Así que tienes que decidir qué importa aquí: una página de cara al usuario vive o muere por la latency; un pipeline de datos en segundo plano se preocupa por el throughput. Nombrar tu objetivo real te evita optimizar el número que no le importa a tus usuarios.
La latency es la espera por una operación; el throughput es cuánto manejas por segundo. Son objetivos independientes — y el batching a menudo cambia latency por throughput.
El rendimiento no se reparte por igual. Casi siempre, una parte domina el tiempo — y optimizar cualquier otra cosa es esfuerzo malgastado hasta que arregles esa.
Una parte lenta suele dominar
Una cadena es exactamente tan fuerte como su eslabón más débil — reforzar los eslabones fuertes no sirve de nada; solo el débil decide si aguanta.
En la mayoría de los sistemas lentos, un único bottleneck acapara el grueso del tiempo — una consulta, un servicio lento, un bucle malo. Todo lo demás ya es bastante rápido. Por esto adivinar es tan malgastador: optimiza una parte que es solo el 2% del tiempo de ejecución y lo máximo que puedes ganar es un 2%. Encuentra la parte que es el 80%, y arreglarla lo transforma todo. Toda la habilidad consiste en localizar el coste dominante, no en mejorar las partes rápidas.
El sistema es tan rápido como su paso más lento
Una autopista despejada hasta un corte de carril donde cada coche avanza a paso de tortuga — la velocidad de todo el trayecto la fija ese único cuello de botella, no los tramos libres.
Una petición que fluye por diez pasos está limitada por el más lento. Acelerar los nueve pasos rápidos apenas mueve el total; el paso lento marca el ritmo. Así que apuntas específicamente al cuello de botella. Y fíjate en que arreglar un bottleneck a menudo revela el siguiente: quita el paso más lento y emerge un nuevo paso más lento. El trabajo de rendimiento es encontrar y despejar iterativamente lo que en ese momento sea el paso limitante.
Optimizar en otra parte es esfuerzo malgastado
Sacar brillo al trofeo mientras el coche tiene un neumático pinchado — esfuerzo gastado donde no cambia nada, mientras lo que de verdad te detiene queda ignorado.
El tiempo dedicado a optimizar cualquier cosa que no sea el bottleneck es, por definición, tiempo que no puede ayudar de forma significativa. A menudo empeora las cosas, añadiendo complejidad y bugs por una ganancia invisible. Esta es la disciplina que impone la medición: te impide optimizar con cariño la parte que entiendes y te obliga a ir a la parte que de verdad cuesta. Arregla el bottleneck, ignora el resto — hasta que el resto se convierta en el bottleneck.
Un bottleneck suele dominar el tiempo, y el sistema es tan rápido como su paso más lento. Arregla esa única cosa; optimizar cualquier otra es malgastar hasta que se convierta en el bottleneck.
Cuando sí encuentras el bottleneck, el arreglo suele ser uno de un puñado de clásicos. Esos pocos patrones explican la inmensa mayoría de las aceleraciones del mundo real.
Un mejor algorithm le gana a una máquina más rápida
Dos rutas a la misma ciudad: un coche más rápido por la carretera larga y sinuosa aún pierde frente a un coche más lento por el atajo recto. La ruta importa más que el motor.
Las mayores victorias a menudo vienen de un mejor algorithm o estructura de datos, no de hardware más rápido ni de microajustes. Un bucle O(n²) sustituido por una búsqueda en hash-map O(n) puede convertir minutos en milisegundos — un cambio que ninguna máquina más rápida podría igualar. Antes de optimizar lo pequeño, pregúntate si el enfoque está mal. Una mejor forma, como muestra el curso de estructuras de datos, es la aceleración más barata y más grande que existe.
Deja de hacer tantos round trips
Hacer veinte viajes separados a la tienda por un artículo cada vez, en lugar de un viaje con una lista — el desplazamiento, no la compra, se está comiendo tu tarde.
Una enorme cantidad de lentitud son demasiados round trips — llamadas repetidas a una base de datos o servicio, cada una barata por sí sola pero devastadora en masa. El clásico es la consulta N+1: traer una lista y luego hacer una consulta más por cada elemento, convirtiendo un viaje en cientos. El arreglo es traer en bloque — pedirlo todo de una vez. Reducir el número de viajes suele ganarle a acelerar cada uno, porque el round trip en sí es el coste.
No hagas trabajo que puedes saltar o reutilizar
Recocinar una comida desde cero cada vez que alguien la pide, en lugar de mantener una olla caliente — el trabajo más rápido es el que no repites.
Dos clásicos más. El caching — recordar un resultado caro para no recalcularlo — es una de las mayores palancas que existen (su propio curso). Y hacer menos: calcula las cosas de forma perezosa, solo cuando hacen falta de verdad; evita cargar datos que no usarás; sáltate trabajo cuyo resultado vas a tirar. La operación más rápida es la que nunca ejecutas. A menudo la mejor optimización no es hacer el trabajo más rápido — es no hacerlo en absoluto.
La mayoría de las aceleraciones son unos pocos clásicos: un mejor algorithm, menos round trips (mata el N+1), caching de resultados caros y simplemente hacer menos trabajo. El trabajo más rápido es el que te saltas.
Cómo mides el rendimiento puede mentirte. El average es el número más reconfortante y más engañoso, y aprender a mirar más allá de él es lo que separa el trabajo de rendimiento real de la ilusión.
El average esconde los casos lentos
Una sala con nueve personas cómodas y una en llamas tiene una temperatura media agradable — la media borra el caso que de verdad importa.
Informar del rendimiento como un average es peligrosamente tranquilizador: mezcla la mayoría rápida con los pocos lentos en un único número feliz. Pero los usuarios no experimentan el average — cada usuario experimenta su petición, y los lentos sienten cada milisegundo. Un tiempo de respuesta promedio de 200ms puede esconder que uno de cada veinte usuarios espera cinco segundos. La media te dice que el sistema va bien mientras una porción real de tus usuarios sufre.
Los percentiles muestran la experiencia real
Un informe que dice no solo la espera típica sino «el 1% más lento de la gente esperó esto» — nombrando las malas experiencias en lugar de promediarlas hasta desaparecer.
Los percentiles capturan lo que el average esconde. p50 (la mediana) es el caso típico — la mitad son más rápidos. p99 es la cola lenta — el 99% son más rápidos, así que es más o menos la peor experiencia que sufre un usuario real. Vigilar el p99, no solo el average, es como ves a los usuarios que de verdad están sufriendo. La cola es donde vive el dolor, y los percentiles son cómo lo haces visible.
La tail latency es lo que los usuarios recuerdan
Una comida terrible en un restaurante pesa más en el recuerdo de alguien que una docena de buenas — las peores experiencias, no el promedio, moldean cómo te juzga la gente.
Las peticiones lentas — la cola — moldean de forma desproporcionada cómo perciben los usuarios tu producto, porque una mala experiencia se queda. Y a escala, la cola alcanza a más gente de lo que parece: si cada página hace muchas llamadas, la probabilidad de que al menos una sea lenta crece deprisa, así que casi todos los usuarios acaban sintiendo la cola. Por esto los equipos serios fijan objetivos sobre p99, no sobre promedios: domar los peores casos es lo que hace que un producto se sienta fiablemente rápido.
Los promedios esconden los casos lentos que los usuarios sí sienten. Vigila los percentiles — p50 para lo típico, p99 para la cola dolorosa — porque las peores experiencias son las que la gente recuerda.
Hay un modo de fallo opuesto a ignorar el rendimiento: perseguirlo demasiado pronto, en todas partes, antes de saber que importa. La famosa advertencia al respecto es famosa por algo.
La premature optimization es la raíz de todo mal
Reforzar cada pared de una casa contra un terremoto que quizá nunca llegue, antes de que nadie se haya mudado siquiera — un esfuerzo enorme gastado en un problema que aún no tienes.
La famosa frase — «premature optimization is the root of all evil» — advierte contra optimizar antes de saber qué necesita algo, o si lo necesita. Optimizar código que no es lento, o que ni siquiera se ejecuta mucho, no compra nada y cuesta mucho: complejidad, bugs y tiempo robado a lo que importa. La mayoría del código no necesita ser rápido; necesita ser correcto y claro. El trabajo de velocidad es para las partes que la medición demuestra que están calientes.
Claro y correcto primero, rápido donde cuenta
Escribes la receta para que cualquiera pueda seguirla, y solo afinas el único paso que haces cien veces al día — no cada paso, solo el caliente.
El orden es: hazlo correcto, hazlo claro y hazlo rápido solo donde la medición demuestra que importa. El código claro es más fácil de optimizar después precisamente porque puedes entenderlo; el código «rápido» enredado es difícil de arreglar cuando el bottleneck real aparece en otra parte. Optimizar para la velocidad casi siempre cuesta legibilidad, así que pagas ese coste solo donde compra una victoria real y medida — y mantienes el resto simple.
Pero no ignores el rendimiento por diseño
No haces a prueba de terremotos un cobertizo, pero tampoco construyes un rascacielos sobre arena — algunas decisiones son baratas de acertar pronto y ruinosas de arreglar tarde.
La advertencia no es «nunca pienses en el rendimiento». Elegir un algorithm O(n²) donde uno O(n) era igual de fácil, o una estructura que no escalará, es un error de diseño que pagarás muy caro después. El equilibrio: no micro-optimices pronto, pero no tomes decisiones arquitectónicas que sean lentas por diseño y caras de deshacer. Acierta la gran forma de forma barata desde el principio; optimiza los detalles solo cuando la medición lo exija.
La premature optimization cuesta claridad por una velocidad que nadie necesita. Hazlo correcto y claro primero, rápido solo donde la medición demuestre que importa — pero no elijas una forma lenta por diseño de la que te arrepentirás.
El rendimiento bien hecho es un método tranquilo y repetible, no una carrera heroica. Toda la práctica cabe en un bucle corto que ejecutas solo cuando hay una razón real para ello.
El bucle: mide, arregla el bottleneck, repite
Un mecánico diagnostica con instrumentos, arregla la única pieza que falla y vuelve a probar — nunca cambia piezas al azar con la esperanza de que el ruido desaparezca.
El método es simple y disciplinado: mide para encontrar el bottleneck, arregla esa única cosa, vuelve a medir para confirmar y para revelar el siguiente bottleneck, y para cuando sea bastante rápido. Cada pasada apunta al coste dominante actual e ignora todo lo demás. Este bucle mantiene el trabajo de rendimiento honesto y eficiente — siempre sabes que estás trabajando en lo que de verdad importa, y siempre sabes si ayudaste.
Fija un objetivo, luego para
Aíslas la casa hasta que está bastante caliente, y luego paras — no sigues añadiendo aislamiento para siempre más allá del punto en que nadie lo nota.
El rendimiento no tiene un final natural — siempre puedes hacer algo marginalmente más rápido. Así que fija un objetivo («páginas por debajo de 300ms en p99») y para cuando lo alcances. Sin una meta, la optimización se convierte en un sumidero de tiempo sin fin con rendimientos decrecientes, y la complejidad se cuela por ganancias que nadie nota. Saber qué significa «bastante rápido» — para tus usuarios, tu caso de uso — es lo que te deja hacer exactamente el trabajo de rendimiento justo y luego ir a construir otra cosa.
- ¿He medido adónde se va realmente el tiempo, o estoy adivinando? - ¿Latency o throughput — qué tipo de rápido necesito en realidad? - ¿Cuál es el bottleneck — la única parte que domina el tiempo? - ¿Es un clásico — mal algorithm, demasiados round trips, cache que falta, trabajo innecesario? - ¿Estoy vigilando el p99, no solo el average que esconde la cola lenta?
- ¿Hay un objetivo hacia el que optimizo, para saber cuándo parar?
- profiler / measure-fix-measure — encontrar el coste real, y el bucle que confirma un arreglo. - latency / throughput — la espera por una cosa, frente al volumen por segundo. - bottleneck — la única parte lenta que domina el tiempo total. - algorithm / round trips / N+1 / caching — las fuentes clásicas de la lentitud, y sus arreglos. - average / percentile / p50 / p99 / tail latency — por qué la media miente y la cola duele. - premature optimization — perseguir velocidad antes de que la medición demuestre que la necesitas. - batching — agrupar trabajo para subir el throughput, a costa de la latency.
- Mides antes de tocar nada, y vuelves a medir después. - Arreglas el bottleneck e ignoras las partes que ya son bastante rápidas. - Tus victorias vienen de algorithms, menos round trips y caching — no de microajustes. - Juzgas la velocidad por p99, no por un average halagador. - Optimizas hacia un objetivo y paras, manteniendo el resto claro y correcto.
El rendimiento es un bucle tranquilo: mide, arregla el único bottleneck, vuelve a medir, para en un objetivo. Recúrrelo solo donde esté demostrado que importa, y mantén todo lo demás claro.