Rendimiento

Medir con contadores y no con el reloj: copias invisibles, reserve, caché y localidad, AoS o SoA, string_view y span, el contenedor justo, lo que hace el compilador y perfilar.

12 aulas · 11 horas de práctica · en el navegador, sin instalar nada

Temario

  1. Medir bien: contadores y no el reloj — Antes de acelerar un programa hay que medirlo, y en el aula el reloj es de mentira. Se mide contando lo que cuesta: dónde va el contador, cuándo se pone en cero, qué carga de trabajo es la de verdad y por qué lo que no contás no existe.
  2. Las copias invisibles — Un parámetro sin &, un for sin &, una variable "mejor" que se pisa, un push_back de algo que no usás más, una lambda que captura por valor: cada uno copia una foto entera sin que nadie escriba la palabra copia. Se cuentan, y se sacan.
  3. reserve y realocaciones — Cada vez que un vector se llena, se muda a un bloque más grande y copia (o mueve) todo lo que tenía. Se cuentan los mudados, se sacan con reserve, se ve el reserve que empeora todo y por qué un noexcept que falta convierte cada mudanza en copias.
  4. Caché y localidad — Dos loops con las mismas operaciones pueden tardar muy distinto: la memoria se trae de a líneas y lo que está cerca ya llegó. Con un modelo de caché que el programa cuenta: recorrer por filas, dar vuelta los loops, sumar columnas sin saltar, el tren desparramado y cortar a tiempo.
  5. Array of structs o struct of arrays — La misma tabla se puede guardar por filas (un vector de structs) o por columnas (un struct de vectores). Con un modelo que mira las direcciones reales: cuántas líneas trae cada forma, pasar a columnas, ordenar sin desalinear, cuándo gana cada una y separar lo caliente de lo frío.
  6. Sacar las reservas de memoria del loop — Pedir memoria es de lo más caro que hace un programa, y un vector declarado adentro de un loop la pide en cada vuelta. Con un contador de pedidos y una fábrica con presupuesto: el banco afuera, clear en vez de uno nuevo, reservar una vez, llenar en vez de devolver y mover en vez de copiar.
  7. string_view y span — Mirar un texto o un pedazo de un vector sin copiarlo: string_view y span son ventanas. Con un string que cuenta sus pedidos y una libreta con hojas contadas: parámetros que no copian, recortar sin pedir memoria, la vista que queda colgando, tramos de un vector con span y buscar en un map sin armar la clave.
  8. Elegir el contenedor justo — Cada contenedor es barato para algunas cosas y caro para otras. Carreras con lo que cuenta el propio contenedor: set para preguntar mucho, deque para meter adelante, un unordered_map tan bueno como su hash, priority_queue para el más grave y lower_bound para los tramos.
  9. Qué optimiza el compilador — El optimizador hace maravillas que el aula no puede mostrar (corre sin optimizar), pero hay cosas que el lenguaje garantiza y sí se ven: cuentas hechas al compilar con constexpr y consteval, la elisión de copias, y lo que el compilador nunca hace por vos, como ordenar los campos de un struct.
  10. Perfilar — Antes de optimizar hay que saber dónde se va el tiempo, y casi nunca es donde uno cree. Un perfilador que anota las operaciones de cada función: encontrar al culpable, separar lo propio de lo que hacen las funciones llamadas, arreglar y volver a medir, la ley de Amdahl y la copia que no estaba en ninguna función.
  11. Taller: optimizá el simulador de partículas — Una fuente de partículas que se juega con el teclado, con tres contadores a la vista: pedidos de memoria, partículas corridas de lugar y copias. Emitir construyendo en el lugar, reservar el presupuesto, borrar sin correr a todas, borrar sin desordenar y recorrer sin copiar.
  12. Taller: la carrera de algoritmos — Cinco problemas clásicos, cada uno con dos caballos: la versión directa y la tuya, con lo que contó cada una. Pares que suman, el más votado, sumas de tramos, Fibonacci con memoria y la ventana que se corre: cambiar el algoritmo le gana a cualquier ajuste.

Es parte de