Power BI · Capítulo 12 · Lección de conceptos · 7 min de lectura

Por qué un informe es lento: dos motores y un modelo comprimido

Cada clic en un segmentador es una consulta que responden dos motores. El de almacenamiento es rápido y trabaja con columnas comprimidas; el de fórmulas es flexible y trabaja fila a fila. Optimizar es, sobre todo, escribir modelos y medidas que mantengan el trabajo en el rápido.

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

  1. Visual Envía una consulta DAX por cada medida
  2. Motor de fórmulas Entiende DAX, trabaja fila a fila, un solo hilo
  3. Motor de almacenamiento Recorre columnas comprimidas, en paralelo, muy rápido
  4. Resultado Los números vuelven al visual

Paso 1 de 4 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.

  1. 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.
  2. 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.
  3. 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.
  4. 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

Cuatro líneas de pedido
order_item_idchannelstatusline_total
19660webcompleted54.99
19663webcompleted189.00
19749webcancelled189.00
19675appcompleted219.96
Web Sales
Web Sales
$243.99

Paso 1 de 4 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.

  1. 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.
  2. Evalúa dos búsquedas con RELATED y 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.
  3. 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.
  4. 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)

0 3,000 6,000 9,000 12,000 Valores distintos channel: 3status: 4discount_pct: 5unit_price: 94product_id: 120customer_id: 1,771order_id: 12,000 channelstatusdiscount_pctunit_priceproduct_idcustomer_idorder_id

Paso 1 de 3 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 channel o status, se reducen a casi nada.

  1. 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 channel o status, se reducen a casi nada.
  2. customer_id tiene 1,771 valores distintos en 12,000 pedidos, y order_id tiene uno por pedido. Una columna de valores únicos no tiene nada que comprimir.
  3. 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.

Web Sales = SUMX(FILTER(order_items, RELATED(orders[channel]) = "web"), order_items[line_total]) es correcta pero lenta. ¿Cuál es la mejor solución?

Elige qué puede ejecutarse en este sitio. Puedes cambiarlo cuando quieras desde "Configurar cookies", al pie de cada página.