La idea
Cada vez que un lector hace clic en un segmentador, cada visual de la página le hace una pregunta al modelo en DAX. La responden dos motores. El motor de almacenamiento es rápido: recorre columnas comprimidas, en paralelo, y devuelve resultados pequeños. El motor de fórmulas es listo: entiende todo DAX, ejecuta iteradores y la lógica rara, y trabaja una fila a la vez. Un informe rápido es aquel donde el motor de almacenamiento hace casi todo el trabajo y el de fórmulas solo une las respuestas.
Esa es la idea única detrás de casi toda mejora de rendimiento. El resto es aprender qué decisiones mantienen el trabajo en el motor rápido y cuáles lo empujan al lento.
Figura 1
Qué pasa cuando se actualiza un visual
- Visual Envía una consulta DAX por cada medida
- Motor de fórmulas Entiende DAX, trabaja fila a fila, un solo hilo
- Motor de almacenamiento Recorre columnas comprimidas, en paralelo, muy rápido
- Resultado Los números vuelven al visual
- Un clic en un segmentador hace que cada visual de la página envíe una consulta DAX. El Performance analyzer (**View → Performance analyzer**) mide cada una.
- El motor de fórmulas recibe la consulta y la planifica. Entiende todo DAX, y también es el más lento de los dos: trabaja una fila a la vez y en un solo hilo.
- Todo lo que se pueda expresar como "recorre estas columnas, filtra por estos valores, suma esta columna" pasa al motor de almacenamiento, que lo hace en paralelo sobre datos comprimidos.
- El motor de almacenamiento devuelve resultados pequeños y el de fórmulas termina el trabajo. Cuantos menos viajes y menos trabajo fila a fila, más rápido el visual.
Por qué existe
La división es la razón de que Power BI pueda recorrer millones de filas en un parpadeo. El motor de almacenamiento (VertiPaq) cambia flexibilidad por velocidad: solo puede hacer cosas simples (filtrar por una columna, sumar una columna, agrupar), y las hace sobre datos guardados columna a columna y comprimidos. Todo lo más elaborado tiene que volver al motor de fórmulas.
Para el autor, eso significa dos tipos de lentitud. Una medida es lenta cuando obliga al motor de fórmulas a recorrer filas. Un modelo es lento cuando está inflado: demasiadas columnas, columnas con demasiados valores distintos, texto largo donde bastaría un número, historia que nadie mira. El "panel lento" de Harbor & Pine casi nunca son los visuales en sí.
Cómo funciona por dentro
Medidas: que el motor de almacenamiento haga el filtrado
La medida lenta clásica recorre una tabla entera con FILTER y evalúa una prueba en cada fila. La
versión rápida expresa el mismo filtro como condiciones de columna dentro de CALCULATE, que el
motor de almacenamiento entiende directamente.
Figura 2
El mismo Web Sales, de dos formas
| order_item_id | channel | status | line_total |
|---|---|---|---|
| 19660 | web | completed | 54.99 |
| 19663 | web | completed | 189.00 |
| 19749 | web | cancelled | 189.00 |
| 19675 | app | completed | 219.96 |
| Web Sales |
|---|
| $243.99 |
SUMX(FILTER(order_items, RELATED(orders[channel]) = "web" && RELATED(orders[status]) = "completed"), order_items[line_total]). El motor de fórmulas visita cada línea por turno.- Evalúa dos búsquedas con
RELATEDy una prueba en cada línea: se queda, se queda, se descarta (cancelada), se descarta (app). En una tabla de 30,338 líneas, ese es el trabajo del motor lento, línea tras línea. CALCULATE(SUM(order_items[line_total]), orders[channel] = "web", orders[status] = "completed")envía dos filtros simples de columna y una suma al motor de almacenamiento, que responde en un solo recorrido.- Los mismos $243.99. Los números coinciden; solo cambia el camino. Por eso una medida lenta puede ser perfectamente correcta.
Recuerda que una medida no se ejecuta una sola vez. Una matriz con seis filas de categoría, un total y un segmentador la evalúa siete veces, y una página con ocho visuales ejecuta esas consultas juntas con cada clic. Un patrón lento que parece inofensivo en una celda se multiplica por cada celda y cada visual, y por eso la misma medida puede sentirse bien en una tarjeta e inutilizable en una matriz grande. Prueba las medidas lentas en el visual más cargado, no en el más simple.
La regla práctica: filtra con CALCULATE(..., tabla[columna] = valor) y reserva FILTER para
condiciones que de verdad necesitan lógica por fila, como comparar dos columnas, y entonces filtra la
tabla más pequeña posible. Los iteradores van bien en tablas pequeñas y con expresiones simples.
Modelo: menos columnas, menor cardinalidad
La compresión depende de la cardinalidad, el número de valores distintos de una columna. El motor de almacenamiento guarda un diccionario de valores y un código por fila, así que una columna con tres valores casi no cuesta nada y una columna de identificadores únicos cuesta lo máximo.
Figura 3
Valores distintos por columna (más = más difícil de comprimir)
- El motor de almacenamiento guarda cada columna como un diccionario de sus valores distintos más un código corto por fila. Las columnas con pocos valores, como
channelostatus, se reducen a casi nada. customer_idtiene 1,771 valores distintos en 12,000 pedidos, yorder_idtiene uno por pedido. Una columna de valores únicos no tiene nada que comprimir.- Cada columna que cargas cuesta memoria y tiempo de recorrido, así que elimina lo que ningún visual usa y guarda los identificadores en columnas enteras, no en texto largo.
Consecuencias prácticas: quita las columnas que ningún visual ni relación necesita, usa números enteros en vez de texto largo para las claves, redondea los decimales que el informe no muestra y separa una fecha con hora en fecha y hora si solo segmentas por fecha. No cargues nunca una columna "por si acaso".
Importaciones: trae solo lo que usa el informe
Una importación ligera filtra filas y elimina columnas en Power Query antes de que lleguen al modelo. Si los pasos se pueden traducir a SQL, Power Query los pliega (query folding) dentro de la consulta al origen, de modo que la base de datos hace el trabajo y solo viaja el resultado reducido. Compruébalo con clic derecho en un paso aplicado: si aparece View Native Query, se pliega. Un paso que rompe el plegado (una función personalizada, una columna de índice) hace que todo lo que viene después se ejecute en tu máquina, así que haz los filtros y la eliminación de columnas antes.
Modo de almacenamiento y relaciones
Import copia los datos a VertiPaq y es lo más rápido. DirectQuery deja los datos en el origen y envía una consulta con cada clic: sirve para datos demasiado grandes o demasiado frescos para importar, y es más lento. Un modelo compuesto mezcla los dos. Entre tablas, prefiere relaciones de dirección única y de muchos a uno: son más rápidas y no son ambiguas, y una bidireccional puede hacer que los filtros viajen por caminos que no querías.
VAR: calcúlalo una sola vez
Si una medida usa la misma expresión tres veces, DAX puede calcularla tres veces. VAR da nombre al
resultado y lo usa donde lo nombres, así que se calcula una sola vez y además la fórmula se lee
mejor.
Dónde medirlo en Power BI Desktop
Abre View → Performance analyzer, empieza a grabar, haz clic por la página y lee los tiempos: consulta DAX, visualización del visual y otros. Copy query pega el DAX en DAX query view o en DAX Studio. Cambia una sola cosa a la vez y mide de nuevo, porque la única forma fiable de saber que una mejora funcionó es un número.
Errores comunes
"Si los números son correctos, la medida está bien"
Corrección y velocidad son propiedades distintas. La versión SUMX(FILTER(...)) y la versión con
CALCULATE devuelven los mismos $243.99 y exigen cantidades de trabajo muy distintas. Un informe
puede ser correcto en cada celda y aun así ser uno que nadie abre.
"Un informe lento necesita una máquina más grande o una capacidad Premium"
Más hardware hace menos malo un mal patrón, pero el costo crece con los datos. Un modelo con columnas innecesarias y medidas fila a fila será lento también en una máquina más grande, solo que más tarde. Arregla primero el patrón; es más barato y dura más.
"DirectQuery es más rápido porque no se copian los datos"
No copiar datos ahorra tiempo de actualización y memoria, no tiempo de consulta. Cada clic se convierte en una consulta a otro sistema a través de una red. Import con un tamaño razonable casi siempre es más rápido para el lector.
Ponte a prueba
Una pregunta. Al responderla, la lección queda marcada en el mapa; nunca bloquea un nivel.
No del todo. Una copia del canal en cada línea engorda el modelo con una columna de mucho volumen y sigue recorriendo la tabla fila a fila. El costo está en el patrón, no en RELATED.Prueba otra respuesta.
Correcto. Un filtro simple de columna es algo que el motor de almacenamiento resuelve en un solo recorrido, sin trabajo fila a fila. Mismos números, mucho menos trabajo para el motor de fórmulas.
No del todo. DirectQuery suele hacer más lenta una medida, porque cada clic se convierte en una consulta a otro sistema. No cambia el patrón fila a fila.Prueba otra respuesta.
No del todo. VAR calcula una expresión una sola vez, lo cual ayuda cuando se usa varias veces. Aquí se usa una vez, y la parte lenta correría exactamente igual.Prueba otra respuesta.
Lección hecha: ya está marcada en el mapa.